AFL源码分析之afl-fuzz.c

作为 AFL 中最重要的一个部分,也就是 fuzzer 实现的关键步骤。因为篇幅太长,会将源码分成好几个模块来介绍

准备工作

gettimeofday() 用来获取当前准确时间,接着用 srandom 根据当前时间与当前进程异或之后获取随机种子。

getopt() 函数读取参数并作出相应处理。

setup_signal_handlers()

调用sigaction,设置信号处理函数

信号 作用
SIGHUP/SIGINT/SIGTERM 处理各种“stop”情况
SIGALRM 处理超时的情况
SIGWINCH 处理窗口大小
SIGUSER1 用户自定义信号,这里定义为skip request
SIGSTP/SIGPIPE 不是很重要的一些信号,可以不用关心
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
EXP_ST void setup_signal_handlers(void) {
//创建sigaction结构体变量sa
struct sigaction sa;

sa.sa_handler = NULL;
sa.sa_flags = SA_RESTART;
sa.sa_sigaction = NULL;

sigemptyset(&sa.sa_mask);//初始化mask为空

/* Various ways of saying "stop". */
/*'stop'情况的处理*/

sa.sa_handler = handle_stop_sig;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);

/* Exec timeout notifications. */
/*超时情况的处理*/

sa.sa_handler = handle_timeout;
sigaction(SIGALRM, &sa, NULL);

/* Window resize */
/*窗口大小的处理*/

sa.sa_handler = handle_resize;
sigaction(SIGWINCH, &sa, NULL);

/* SIGUSR1: skip entry */
/*自定义信号处理*/

sa.sa_handler = handle_skipreq;
sigaction(SIGUSR1, &sa, NULL);

/* Things we don't care about. */
/*一些不是很重要的信号的处理*/

sa.sa_handler = SIG_IGN;
sigaction(SIGTSTP, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);

}
-----------------------------------------------------------
static void handle_stop_sig(int sig) {
//设置stop_soon为1
stop_soon = 1;
//child_pid存在,发送SIGKILL信号,从而被系统杀死
if (child_pid > 0) kill(child_pid, SIGKILL);
//forksrv_pid存在,同理
if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL);

}
-----------------------------------------------------------
static void handle_timeout(int sig) {
//如果child_pid存在
if (child_pid > 0) {
//设置child_timed_out为1,并kill掉child_pid
child_timed_out = 1;
kill(child_pid, SIGKILL);
//如果child_pid不存在且forksrv_pid>0
} else if (child_pid == -1 && forksrv_pid > 0) {
//设置child_timed_out为1,并kill掉forksrv_pid
child_timed_out = 1;
kill(forksrv_pid, SIGKILL);

}

}
-----------------------------------------------------------
static void handle_resize(int sig) {
clear_screen = 1;
}
-----------------------------------------------------------
static void handle_skipreq(int sig) {

skip_requested = 1;

}

check_asan_opts()

读取环境变量ASAN_OPTIONSMSAN_OPTIONS,并做相应的检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static void check_asan_opts(void) {
u8* x = getenv("ASAN_OPTIONS");//读取ASAN_OPTIONS

if (x) {
//检查abort_on_error是否为1,不为1报错
if (!strstr(x, "abort_on_error=1"))
FATAL("Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!");

if (!strstr(x, "symbolize=0"))
FATAL("Custom ASAN_OPTIONS set without symbolize=0 - please fix!");

}//ASAN_OPTIONS情况

x = getenv("MSAN_OPTIONS");//读取MSAN_OPTIONS

if (x) {
//检查exit_code对应的状态码是否设置,没有报错
if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR)))
FATAL("Custom MSAN_OPTIONS set without exit_code="
STRINGIFY(MSAN_ERROR) " - please fix!");
//检查symbolize是否为0,没有报错
if (!strstr(x, "symbolize=0"))
FATAL("Custom MSAN_OPTIONS set without symbolize=0 - please fix!");

}//MSAN_OPTIONS情况

}

fix_up_sync()

如果通过 -M或者-S指定了 sync_id,则更新 out_dirsync_dir 的值:设置 sync_dir 的值为 out_dir,设置 out_dir 的值为out_dir/sync_id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
static void fix_up_sync(void) {

u8* x = sync_id;

if (dumb_mode)////如果在设置dumb_mode模式,提示-S / -M参数与 -n参数互斥
FATAL("-S / -M and -n are mutually exclusive");

if (skip_deterministic) {
//如果force_deterministic已设置,检查参数是否互斥
if (force_deterministic)
FATAL("use -S instead of -M -d");
else
FATAL("-S already implies -d");

}

while (*x) {
//ID纠正
if (!isalnum(*x) && *x != '_' && *x != '-')
FATAL("Non-alphanumeric fuzzer ID specified via -S or -M");

x++;

}
//sync_id过长报错
if (strlen(sync_id) > 32) FATAL("Fuzzer ID too long");

x = alloc_printf("%s/%s", out_dir, sync_id);
//更新sync_dir和out_dir的值
sync_dir = out_dir;
out_dir = x;
//如果force_deterministic没设置,赋值
if (!force_deterministic) {
skip_deterministic = 1;
use_splicing = 1;
}

}

save_cmdline()

Copy 当前命令行参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static void save_cmdline(u32 argc, char** argv) {

u32 len = 1, i;
u8* buf;

//遍历参数算出长度
for (i = 0; i < argc; i++)
len += strlen(argv[i]) + 1;

//为buf参数开辟存储空间
buf = orig_cmdline = ck_alloc(len);

//将参数拷贝进buf
for (i = 0; i < argc; i++) {
//计算当前argv[i]长度
u32 l = strlen(argv[i]);

memcpy(buf, argv[i], l);
buf += l;
//判断后面是否还有参数
if (i != argc - 1) *(buf++) = ' ';

}
//结尾赋0
*buf = 0;

}

fix_up_banner()

获取目标程序名称或程序路径或路径+省略号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static void fix_up_banner(u8* name) {
//如果没有设置use_banner
if (!use_banner) {
//如果设置了sync_id,赋值
if (sync_id) {
//赋值
use_banner = sync_id;

} else {
//获取参数最后一个'/'后面的内容,即最终文件名
u8* trim = strrchr(name, '/');
//如果没获取到,把文件目录赋给use_banner
if (!trim) use_banner = name;
//如果获取到了,把最终文件名去掉'/'并赋给use_banner
else use_banner = trim + 1;

}

}
//如果use_banner太长
if (strlen(use_banner) > 40) {
//创建一个44字长的tmp变量
u8* tmp = ck_alloc(44);
sprintf(tmp, "%.40s...", use_banner);
//将tmp赋给use_banner
use_banner = tmp;

}

}

check_if_tty()

检查是否在 tty 终端上面运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void check_if_tty(void) {

struct winsize ws;
//如果读取到了环境变量AFL_NO_UI
if (getenv("AFL_NO_UI")) {
OKF("Disabling the UI because AFL_NO_UI is set.");
not_on_tty = 1;
return;
}
//通过ioctl读取window size
if (ioctl(1, TIOCGWINSZ, &ws)) {
//如果报错为ENOTTY,说明当前不在一个tty终端运行
if (errno == ENOTTY) {
OKF("Looks like we're not running on a tty, so I'll be a bit less verbose.");
not_on_tty = 1;
}

return;
}

}

几个CPU检查相关的函数

  • get_core_count():获取 CPU 数量

  • check_crash_handling():确保核心转储不会进入程序

  • check_cpu_governor():检查CPU管理者

setup_post()

如果设置AFL_POST_LIBRARY环境变量,则加载 afl_postprocess() 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static void setup_post(void) {

void* dh;
//获取环境变量AFL_POST_LIBRARY
u8* fn = getenv("AFL_POST_LIBRARY");
u32 tlen = 6;
//如果没获取到,返回
if (!fn) return;

ACTF("Loading postprocessor from '%s'...", fn);
//以RTLD_NOW模式打开AFL_POST_LIBRARY指向的动态链接库,在返回前解析出所有未定义的符号
dh = dlopen(fn, RTLD_NOW);
if (!dh) FATAL("%s", dlerror());

//动态链接库中afl_postprocess()函数地址赋给post_handler
post_handler = dlsym(dh, "afl_postprocess");
if (!post_handler) FATAL("Symbol 'afl_postprocess' not found.");

/* Do a quick test. It's better to segfault now than later =) */

//afl_postprocess()函数测试
post_handler("hello", &tlen);

OKF("Postprocessor installed successfully.");

}

setup_shm()(very important)

用于设置共享内存和 virgin_bits

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
EXP_ST void setup_shm(void) {

u8* shm_str;
//如果in_bitmap为空,初始化virgin_bits[MAP_SIZE]每个值为255
if (!in_bitmap) memset(virgin_bits, 255, MAP_SIZE);

//初始化
memset(virgin_tmout, 255, MAP_SIZE);
memset(virgin_crash, 255, MAP_SIZE);

//调用 shmget 函数分配一块共享内存,并将返回的共享内存标识符保存到 shm_id
shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
/*函数的第一个参数:程序需要提供一个key,为共享内存段命名shmget()函数成功时返回一个与key相关的共享内存标识符(非负整数),
用于后续的共享内存函数。调用失败返回-1。
函数的第二个参数:需要共享的内存容量,这里大小为MAP_SIZE
函数的第三个参数:权限标志
IPC_CREAT 如果共享内存不存在,则创建一个共享内存
IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误
0060代表拥有者具有读写权限
*/

//如果没有获得共享内存标识符,即共享内存创建失败,报错
if (shm_id < 0) PFATAL("shmget() failed");

//注册程序正常终止时删除共享内存
atexit(remove_shm);

//创建shm_str字符串变量,里面存放shm_id
shm_str = alloc_printf("%d", shm_id);

/* If somebody is asking us to fuzz instrumented binaries in dumb mode,
we don't want them to detect instrumentation, since we won't be sending
fork server commands. This should be replaced with better auto-detection
later on, perhaps? */

//如果不是dumb模式,则设置SHM_ENV_VAR为shm_str
if (!dumb_mode) setenv(SHM_ENV_VAR, shm_str, 1);

//释放shm_str指针
ck_free(shm_str);

trace_bits = shmat(shm_id, NULL, 0);
/*启动对该共享内存的访问,将共享内存的首地址赋给trace_bits,并把共享内存连接到当前进程的地址空间
第一个参数,shm_id是由shmget()函数返回的共享内存标识
第二个参数,指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址
第三个参数,标志位,通常为0*/

//共享内存访问启动失败,报错
if (!trace_bits) PFATAL("shmat() failed");

}
-----------------------------------------------------------
static void remove_shm(void) {

shmctl(shm_id, IPC_RMID, NULL);
/*函数用来控制共享内存
第一个参数,shm_id是shmget()函数返回的共享内存标识符
第二个参数,IPC_RMID:删除共享内存段
第三个参数,指向共享内存模式和访问权限的结构,这里指向NULL*/
}

init_count_class16()

统计 AFL 遍历路径的次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static const u8 count_class_lookup8[256] = {
/*记录路径命中次数,因为可能循环5次和循环6次造成的效果一样,所以把它们统一认为是循环了8次*/
[0] = 0,
[1] = 1,
[2] = 2,
[3] = 4,
[4 ... 7] = 8,
[8 ... 15] = 16,
[16 ... 31] = 32,
[32 ... 127] = 64,
[128 ... 255] = 128

};

static u16 count_class_lookup16[65536];


EXP_ST void init_count_class16(void) {

u32 b1, b2;
//将count_class_lookup16[65536]拆分成两个嵌套的256次循环,256x256=65536
for (b1 = 0; b1 < 256; b1++)
for (b2 = 0; b2 < 256; b2++)
/*count_class_lookup8[b1]左移8的结果和count_class_lookup8[b2]进行按位或运算,
再把整个结果放在 count_class_lookup8[b1左移8再加上b2] 中*/
count_class_lookup16[(b1 << 8) + b2] =
(count_class_lookup8[b1] << 8) |
count_class_lookup8[b2];

}

setup_dirs_fds()

准备输出文件夹和文件描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* Prepare output directories and fds. */
EXP_ST void setup_dirs_fds(void) {

u8* tmp;
s32 fd;

ACTF("Setting up output directories...");
/*如果sync_id存在,创建文件夹sync_dir,权限设置为0700(读写执行)
且errno != EEXIST,抛出异常*/
if (sync_id && mkdir(sync_dir, 0700) && errno != EEXIST)
PFATAL("Unable to create '%s'", sync_dir);

//如果创建文件夹out_dir,且权限设置为0700成功
if (mkdir(out_dir, 0700)) {
//如果errno != EEXIST,抛出异常
if (errno != EEXIST) PFATAL("Unable to create '%s'", out_dir);
//调用函数
maybe_delete_out_dir();

} else {//如果创建文件夹out_dir失败

//如果设置了in_place_resume,抛出异常
if (in_place_resume)
FATAL("Resume attempted but old output directory not found");

//以只读模式打开文件并将句柄赋值给out_dir_fd
out_dir_fd = open(out_dir, O_RDONLY);

#ifndef __sun//如果没有定义宏__sun
//如果out_dir文件打开失败,或out_dir文件简历互斥锁定失败,抛出异常
if (out_dir_fd < 0 || flock(out_dir_fd, LOCK_EX | LOCK_NB))
PFATAL("Unable to flock() output directory.");

#endif /* !__sun */

}
//创建 queue 文件夹
/* Queue directory for any starting & discovered paths. */

// 创建 out_dir/queue 文件夹,设置权限为0700
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. */

//创建out_dir/queue/.state/,设置权限为0700
//该文件夹主要保存用于session resume和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. */

//创建out_dir/queue/.state/deterministic_done/,设置权限为0700
//用于标记过去经历过deterministic fuzzing的队列条目
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. */

//创建out_dir/queue/.state/auto_extras/,设置权限为0700
//用于保存带有自动选择的字典条目
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. */

//创建out_dir/queue/.state/redundant_edges/,设置权限为0700
//用于保存当前被认为是多余的路径集合
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. */

//创建out_dir/queue/.state/variable_behavior/,设置权限为0700
//保存不同行为的路径集
tmp = alloc_printf("%s/queue/.state/variable_behavior/", out_dir);
if (mkdir(tmp, 0700)) PFATAL("Unable to create '%s'", tmp);
ck_free(tmp);

/* Sync directory for keeping track of cooperating fuzzers. */

//如果sync_id存在
if (sync_id) {

//创建out_dir/.synced/,设置权限为0700
//做目录同步,该目录用于跟踪cooperating fuzzers
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. */

//创建 out_dir/crashes/ 目录,设置权限为读写执行‘
//记录crash
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);

/* Generally useful file descriptors. */

//用读写的方式打开/dev/null,打不开报错
dev_null_fd = open("/dev/null", O_RDWR);
if (dev_null_fd < 0) PFATAL("Unable to open /dev/null");
//用只读方式打开/dev/urandom,打不开报错
dev_urandom_fd = open("/dev/urandom", O_RDONLY);
if (dev_urandom_fd < 0) PFATAL("Unable to open /dev/urandom");

/* Gnuplot output file. */

//以只写方式打开out_dir/plot_data文件,如果文件不存在就创建,返回句柄fd
tmp = alloc_printf("%s/plot_data", out_dir);
fd = open(tmp, O_WRONLY | O_CREAT | O_EXCL, 0600);
/*O_CREAT:如果指定文件不存在,则创建这个文件
O_EXCL :如果要创建的文件已存在,则返回 -1,并且修改 errno 的值*/

if (fd < 0) PFATAL("Unable to create '%s'", tmp);
ck_free(tmp);

//根据句柄得到FILE结构体指针plot_file
plot_file = fdopen(fd, "w");
if (!plot_file) PFATAL("fdopen() failed");

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 */

}

read_testcases()

从输入文件夹中读取所有文件,然后将它们排队进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
static void read_testcases(void) {

struct dirent **nl;
s32 nl_cnt;
u32 i;
u8* fn;

/* Auto-detect non-in-place resumption attempts. */

//访问in_dir/queue文件夹,如果存在就设置in_dir为in_dir/queue
fn = alloc_printf("%s/queue", in_dir);
if (!access(fn, F_OK)) in_dir = fn; else ck_free(fn);

ACTF("Scanning '%s'...", in_dir);

/* We use scandir() + alphasort() rather than readdir() because otherwise,
the ordering of test cases would vary somewhat randomly and would be
difficult to control. */

//扫描in_dir,并将扫描结果排好序后放入nl,匹配到的文件个数存储在nl_cnt中
nl_cnt = scandir(in_dir, &nl, NULL, alphasort);
/*第一个参数:扫描的路径
第二个参数:扫描结果保存的列表
第三个参数:指向过滤函数,对参数一进行过滤,如果是NULL,则扫描一参目录下的所有顶层文件
第四个参数:将过滤后的结果进行排序*/

//如果没有扫描到匹配文件
if (nl_cnt < 0) {

if (errno == ENOENT || errno == ENOTDIR)

SAYF("\n" cLRD "[-] " cRST
"The input directory does not seem to be valid - try again. The fuzzer needs\n"
" one or more test case to start with - ideally, a small file under 1 kB\n"
" or so. The cases must be stored as regular files directly in the input\n"
" directory.\n");

PFATAL("Unable to open '%s'", in_dir);

}
//如果设置了shuffle_queue,且扫描到了匹配文件
if (shuffle_queue && nl_cnt > 1) {

ACTF("Shuffling queue...");
//利用shuffle_ptrs函数重排nl里的指针位置
shuffle_ptrs((void**)nl, nl_cnt);

}
//遍历nl,nl[i]->d_name的值为input文件夹下的文件名,例如a.out
for (i = 0; i < nl_cnt; i++) {

struct stat st;
//目录
u8* fn = alloc_printf("%s/%s", in_dir, nl[i]->d_name);
u8* dfn = alloc_printf("%s/.state/deterministic_done/%s", in_dir, nl[i]->d_name);

u8 passed_det = 0;

free(nl[i]); /* not tracked */

//从fn获取文件信息并保存在st中,检测目录是否可以访问,访问不了报错
if (lstat(fn, &st) || access(fn, R_OK))
PFATAL("Unable to access '%s'", fn);

/* This also takes care of . and .. */
//判断st.st_mode是否是一个常规文件,st.st_size确定文件大小,strstr匹配文件是否是README.txt
//排除干扰文件,如.或..或README
if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn, "/README.txt")) {

ck_free(fn);
ck_free(dfn);
continue;

}
//如果是有效文件,检查文件大小是否超过规定值界限,默认1024*1024字节,即1M
if (st.st_size > MAX_FILE)
FATAL("Test case '%s' is too big (%s, limit is %s)", fn,
DMS(st.st_size), DMS(MAX_FILE));

/* Check for metadata that indicates that deterministic fuzzing
is complete for this entry. We don't want to repeat deterministic
fuzzing when resuming aborted scans, because it would be pointless
and probably very time-consuming. */

//检查dfn是否创建成功,即in_dir/.state/deterministic_done/nl[i]->d_name
if (!access(dfn, F_OK)) passed_det = 1;
ck_free(dfn);

//将fd文件入队
add_to_queue(fn, st.st_size, passed_det);

}

free(nl); /* not tracked */
//如果没有入队的输入文件
if (!queued_paths) {

SAYF("\n" cLRD "[-] " cRST
"Looks like there are no valid test cases in the input directory! The fuzzer\n"
" needs one or more test case to start with - ideally, a small file under\n"
" 1 kB or so. The cases must be stored as regular files directly in the\n"
" input directory.\n");

FATAL("No usable test cases in '%s'", in_dir);

}

last_path_time = 0;
queued_at_start = queued_paths;

}

load_auto()

load自动生成的提取出来的词典token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
static void load_auto(void) {

u32 i;
//循环遍历从0到USE_AUTO_EXTRAS,默认50
for (i = 0; i < USE_AUTO_EXTRAS; i++) {

u8 tmp[MAX_AUTO_EXTRA + 1];
u8* fn = alloc_printf("%s/.state/auto_extras/auto_%06u", in_dir, i);
s32 fd, len;
//以只读的方式打开fn获得句柄fd
fd = open(fn, O_RDONLY, 0600);

//打开失败报错
if (fd < 0) {

if (errno != ENOENT) PFATAL("Unable to open '%s'", fn);
ck_free(fn);
break;

}

/* We read one byte more to cheaply detect tokens that are too
long (and skip them). */

//从fd读取MAX_AUTO_EXTRA + 1个字节到tmp中
//MAX_AUTO_EXTRA默认32,是最大大小,+1判断读取是否过长
len = read(fd, tmp, MAX_AUTO_EXTRA + 1);
//没读取到报错
if (len < 0) PFATAL("Unable to read from '%s'", fn);
/*如果读取的长度在MIN_AUTO_EXTRA(3)和MAX_AUTO_EXTRA(32)之间
调用maybe_add_auto函数*/
if (len >= MIN_AUTO_EXTRA && len <= MAX_AUTO_EXTRA)
maybe_add_auto(tmp, len);

close(fd);
ck_free(fn);

}

if (i) OKF("Loaded %u auto-discovered dictionary tokens.", i);
else OKF("No auto-generated dictionary tokens to reuse.");

}