yangguangmamama 1 month ago
parent 2fc8b00adc
commit a5ec96676c

@ -98,7 +98,7 @@ static volatile u8
/* Classify tuple counts. This is a slow & naive version, but good enough here. */
//用于对执行路径中的元组计数进行分类
static u8 count_class_lookup[256] = {
[0] = 0,
@ -113,7 +113,6 @@ static u8 count_class_lookup[256] = {
};
//对内存中的元组计数进行分类。如果edges_only标志被设置则只标记边界情况否则使用count_class_lookup数组对每个字节的计数进行分类
static void classify_counts(u8* mem) {
u32 i = MAP_SIZE;
@ -138,7 +137,7 @@ static void classify_counts(u8* mem) {
/* See if any bytes are set in the bitmap. */
//检查在追踪位图中是否有任何字节被设置。如果有则返回1表示有变化如果没有则返回0。
static inline u8 anything_set(void) {
u32* ptr = (u32*)trace_bits;
@ -152,7 +151,7 @@ static inline u8 anything_set(void) {
/* Get rid of shared memory and temp files (atexit handler). */
//用于在程序退出时清理共享内存和临时文件。它会尝试删除程序输入文件并调用shmctl来删除共享内存标识符
static void remove_shm(void) {
unlink(prog_in); /* Ignore errors */
@ -162,7 +161,7 @@ static void remove_shm(void) {
/* Configure shared memory. */
//配置共享内存。创建一个共享内存段并将其ID设置到环境变量中以便子进程可以使用。同时将trace_bits指向共享内存的起始地址
static void setup_shm(void) {
u8* shm_str;
@ -187,7 +186,7 @@ static void setup_shm(void) {
/* Read initial file. */
//读取要分析的初始文件并将其内容存储在in_data中。同时记录文件的长度并检查文件大小是否在允许的范围内
static void read_initial_file(void) {
struct stat st;
@ -214,7 +213,7 @@ static void read_initial_file(void) {
/* Write output file. */
//将内存中的数据写入到指定的文件路径。如果文件已存在,则先删除。然后创建新文件,并将数据写入
static s32 write_to_file(u8* path, u8* mem, u32 len) {
s32 ret;
@ -235,7 +234,7 @@ static s32 write_to_file(u8* path, u8* mem, u32 len) {
/* Handle timeout signal. */
//处理超时信号。设置child_timed_out标志并尝试杀死子进程
static void handle_timeout(int sig) {
child_timed_out = 1;
@ -246,7 +245,7 @@ static void handle_timeout(int sig) {
/* Execute target application. Returns exec checksum, or 0 if program
times out. */
//执行目标程序并返回执行校验和包含fork()子进程、设置限制、等待结果等逻辑
static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
static struct itimerval it;
@ -371,7 +370,7 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) {
#ifdef USE_COLOR
/* Helper function to display a human-readable character. */
//显示一个可读的字符。如果字符是控制字符或非打印字符,则显示其十六进制代码,解释并报告输入文件中的模式
static void show_char(u8 val) {
switch (val) {
@ -387,7 +386,7 @@ static void show_char(u8 val) {
/* Show the legend */
//显示图例,解释不同颜色和标记的含义
static void show_legend(void) {
SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block "
@ -404,7 +403,7 @@ static void show_legend(void) {
/* Interpret and report a pattern in the input file. */
//解释并报告输入文件中的模式。它分析文件中的每个字节,并根据其对程序执行路径的影响对其进行分类
static void dump_hex(u8* buf, u32 len, u8* b_data) {
u32 i;
@ -559,7 +558,7 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) {
/* Actually analyze! */
//核心分析逻辑,实际执行分析。它通过改变输入文件中的字节并观察对执行路径的影响来推断文件格式的结构
static void analyze(char** argv) {
u32 i;
@ -574,7 +573,6 @@ static void analyze(char** argv) {
#ifdef USE_COLOR
show_legend();
//设置基本环境,包括文件描述符、环境变量等
#endif /* USE_COLOR */
for (i = 0; i < in_len; i++) {
@ -654,7 +652,7 @@ static void analyze(char** argv) {
/* Handle Ctrl-C and the like. */
//处理停止信号如Ctrl-C。设置stop_soon标志并尝试杀死子进程
static void handle_stop_sig(int sig) {
stop_soon = 1;
@ -665,7 +663,7 @@ static void handle_stop_sig(int sig) {
/* Do basic preparations - persistent fds, filenames, etc. */
//进行基本的准备工作,包括设置持久的文件描述符、文件名等,并配置环境变量
static void set_up_environment(void) {
u8* x;
@ -735,7 +733,7 @@ static void set_up_environment(void) {
/* Setup signal handlers, duh. */
//设置信号处理程序,以便处理停止和超时信号
static void setup_signal_handlers(void) {
struct sigaction sa;
@ -762,7 +760,7 @@ static void setup_signal_handlers(void) {
/* Detect @@ in args. */
//检测@@在参数中的位置,并替换为实际的文件路径
static void detect_file_args(char** argv) {
u32 i = 0;
@ -804,7 +802,7 @@ static void detect_file_args(char** argv) {
/* Display usage hints. */
//显示使用提示
static void usage(u8* argv0) {
SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
@ -838,7 +836,7 @@ static void usage(u8* argv0) {
/* Find binary. */
//查找并验证二进制文件的路径
static void find_binary(u8* fname) {
u8* env_path = 0;
@ -891,7 +889,7 @@ static void find_binary(u8* fname) {
/* Fix up argv for QEMU. */
//为QEMU模式修复argv设置QEMU的参数
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
@ -954,7 +952,7 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
/* Main entry point */
//程序的主入口点。处理命令行参数,设置环境,执行分析,并输出结果
int main(int argc, char** argv) {
s32 opt;

@ -56,19 +56,19 @@
#include <sys/wait.h>
#include <sys/time.h>
static u8** as_params; /* Parameters passed to the real 'as' */ //传递给as的参数数组
static u8** as_params; /* Parameters passed to the real 'as' */
static u8* input_file; /* Originally specified input file */
static u8* modified_file; /* Instrumented file for the real 'as' */ //用于as的插有instrumentation的文件
static u8* input_file; /* Originally specified input file */
static u8* modified_file; /* Instrumented file for the real 'as' */
static u8 be_quiet, /* Quiet mode (no stderr output) */ //是否开启安静模式不输出到stderr
clang_mode, /* Running in clang mode? */ // 是否在clang模式下运行
pass_thru, /* Just pass data through? */ //是否只是简单地传递数据
just_version, /* Just show version? */ //是否只显示版本号
sanitizer; /* Using ASAN / MSAN */ //是否使用ASAN或MSAN
static u8 be_quiet, /* Quiet mode (no stderr output) */
clang_mode, /* Running in clang mode? */
pass_thru, /* Just pass data through? */
just_version, /* Just show version? */
sanitizer; /* Using ASAN / MSAN */
static u32 inst_ratio = 100, /* Instrumentation probability (%) */ //Instrumentation概率%
as_par_cnt = 1; /* Number of params to 'as' */ //传递给as的参数数量
static u32 inst_ratio = 100, /* Instrumentation probability (%) */
as_par_cnt = 1; /* Number of params to 'as' */
/* If we don't find --32 or --64 in the command line, default to
instrumentation for whichever mode we were compiled with. This is not
@ -92,7 +92,7 @@ static u8 use_64bit = 0;
/* Examine and modify parameters to pass to 'as'. Note that the file name
is always the last parameter passed by GCC, so we exploit this property
to keep the code simple. */
//处理和修改传递给asGNU assembler的参数。这个函数会检查命令行参数并根据这些参数来设置全局变量如use_64bit和as_params
static void edit_params(int argc, char** argv) {
u8 *tmp_dir = getenv("TMPDIR"), *afl_as = getenv("AFL_AS");
@ -129,7 +129,7 @@ static void edit_params(int argc, char** argv) {
/* Although this is not documented, GCC also uses TEMP and TMP when TMPDIR
is not set. We need to check these non-standard variables to properly
handle the pass_thru logic later on. */
//检查TMPDIR、AFL_AS等环境变量确定临时目录和汇编器路径
if (!tmp_dir) tmp_dir = getenv("TEMP");
if (!tmp_dir) tmp_dir = getenv("TMP");
if (!tmp_dir) tmp_dir = "/tmp";
@ -148,9 +148,7 @@ static void edit_params(int argc, char** argv) {
#ifdef __APPLE__
/* The Apple case is a bit different... */
//这段代码检查命令行参数中是否有 -arch并检查其后的参数。
//如果 -arch 后面是 x86_64则设置 use_64bit 为 1表示使用 64 位模式。
//如果 -arch 后面是 i386则输出错误信息并终止程序因为 32 位的 Apple 平台不被支持
if (!strcmp(argv[i], "-arch") && i + 1 < argc) {
if (!strcmp(argv[i + 1], "x86_64")) use_64bit = 1;
@ -161,12 +159,12 @@ static void edit_params(int argc, char** argv) {
/* Strip options that set the preference for a particular upstream
assembler in Xcode. */
//如果当前处于 clang 模式,并且命令行参数中有 -q 或 -Q则跳过这些参数不将它们传递给汇编器
if (clang_mode && (!strcmp(argv[i], "-q") || !strcmp(argv[i], "-Q")))
continue;
#endif /* __APPLE__ */
//将当前处理的命令行参数 argv[i] 添加到 as_params 数组中as_par_cnt 用于记录已添加的参数数量
as_params[as_par_cnt++] = argv[i];
}
@ -175,9 +173,7 @@ static void edit_params(int argc, char** argv) {
/* When calling clang as the upstream assembler, append -c -x assembler
and hope for the best. */
//如果 use_clang_as 为真(即在 macOS 下使用 clang 作为汇编器)
//则向 as_params 数组中添加 -c、-x 和 assembler 参数
//以确保 clang 正确处理汇编文件
if (use_clang_as) {
as_params[as_par_cnt++] = "-c";
@ -187,9 +183,7 @@ static void edit_params(int argc, char** argv) {
}
#endif /* __APPLE__ */
//如果是 --version则设置 just_version 为 1并跳转到 wrap_things_up直接返回版本信息。
//如果输入文件以 - 开头但不是 --version则输出错误信息并终止程序。
//如果输入文件是单独的 -,则将 input_file 设置为 NULL表示从标准输入读取
input_file = argv[argc - 1];
if (input_file[0] == '-') {
@ -209,17 +203,16 @@ static void edit_params(int argc, char** argv) {
to compile a program, rather than using gcc on an ad-hoc .s file in
a format we may not understand. This works around an issue compiling
NSS. */
//检查输入文件是否位于临时目录(如 /tmp 或 /var/tmp
//如果输入文件不在临时目录中,则设置 pass_thru 为 1表示直接传递文件内容而不进行插桩
if (strncmp(input_file, tmp_dir, strlen(tmp_dir)) &&
strncmp(input_file, "/var/tmp/", 9) &&
strncmp(input_file, "/tmp/", 5)) pass_thru = 1;
}
//生成一个临时文件名,用于存储插桩后的汇编文件。文件名格式为 tmp_dir/.afl-PID-TIMESTAMP.s
modified_file = alloc_printf("%s/.afl-%u-%u.s", tmp_dir, getpid(),
(u32)time(NULL));
//将生成的临时文件名添加到 as_params 数组中,作为汇编器的输入文件,并将数组末尾设置为 NULL表示参数列表结束
wrap_things_up:
as_params[as_par_cnt++] = modified_file;
@ -230,13 +223,9 @@ wrap_things_up:
/* Process input file, generate modified_file. Insert instrumentation in all
the appropriate places. */
//处理输入文件并生成一个修改过的文件modified_file在其中插入 instrumentation仪器化代码。这个函数会读取原始的汇编文件根据配置插入instrumentation代码并将结果写入新的文件
static void add_instrumentation(void) {
//line[MAX_LINE]:用于存储从输入文件中读取的每一行。
//inf 和 outf分别表示输入文件和输出文件的指针。
//outfd输出文件的文件描述符。
//ins_lines记录插桩的行数。
static u8 line[MAX_LINE];
FILE* inf;
@ -252,16 +241,14 @@ static void add_instrumentation(void) {
u8* colon_pos;
#endif /* __APPLE__ */
//打开输入文件以供读取。
//如果 input_file 不为空,则打开该文件;否则从标准输入读取
if (input_file) {
inf = fopen(input_file, "r");
if (!inf) PFATAL("Unable to read '%s'", input_file);
} else inf = stdin;
//创建并打开输出文件以供写入。
//使用 open 创建文件,并使用 fdopen 将其转换为 FILE* 类型
outfd = open(modified_file, O_WRONLY | O_EXCL | O_CREAT, 0600);
if (outfd < 0) PFATAL("Unable to write to '%s'", modified_file);
@ -269,15 +256,14 @@ static void add_instrumentation(void) {
outf = fdopen(outfd, "w");
if (!outf) PFATAL("fdopen() failed");
//
while (fgets(line, MAX_LINE, inf)) {
/* In some cases, we want to defer writing the instrumentation trampoline
until after all the labels, macros, comments, etc. If we're in this
mode, and if the line starts with a tab followed by a character, dump
the trampoline now. */
//在适当的位置插入插桩代码。
//如果满足条件(如不在跳过模式、处于 .text 段、需要插桩等),则插入插桩代码,并增加插桩行数
if (!pass_thru && !skip_intel && !skip_app && !skip_csect && instr_ok &&
instrument_next && line[0] == '\t' && isalpha(line[1])) {
@ -290,17 +276,15 @@ static void add_instrumentation(void) {
}
/* Output the actual line, call it a day in pass-thru mode. */
//将当前行写入输出文件。
fputs(line, outf);
//如果处于 pass_thru 模式,则跳过后续处理
if (pass_thru) continue;
/* All right, this is where the actual fun begins. For one, we only want to
instrument the .text section. So, let's keep track of that in processed
files - and let's set instr_ok accordingly. */
//检测并处理 .text 段。
//如果当前行表示 .text 段,则设置 instr_ok 为 1表示可以插桩。
//如果当前行表示其他段(如 .bss 或 .data则设置 instr_ok 为 0表示跳过插桩
if (line[0] == '\t' && line[1] == '.') {
/* OpenBSD puts jump tables directly inline with the code, which is
@ -331,8 +315,7 @@ static void add_instrumentation(void) {
/* Detect off-flavor assembly (rare, happens in gdb). When this is
encountered, we set skip_csect until the opposite directive is
seen, and we do not instrument. */
//处理 .code 指令。
//如果检测到 .code32 或 .code64则根据当前模式设置 skip_csect跳过插桩
if (strstr(line, ".code")) {
if (strstr(line, ".code32")) skip_csect = use_64bit;
@ -342,14 +325,12 @@ static void add_instrumentation(void) {
/* Detect syntax changes, as could happen with hand-written assembly.
Skip Intel blocks, resume instrumentation when back to AT&T. */
//处理汇编语法变化。
//如果检测到 .intel_syntax则跳过插桩如果检测到 .att_syntax则恢复插桩
if (strstr(line, ".intel_syntax")) skip_intel = 1;
if (strstr(line, ".att_syntax")) skip_intel = 0;
/* Detect and skip ad-hoc __asm__ blocks, likewise skipping them. */
//处理 #APP 和 #NO_APP 块。
//如果检测到 #APP则跳过插桩如果检测到 #NO_APP则恢复插桩
if (line[0] == '#' || line[1] == '#') {
if (strstr(line, "#APP")) skip_app = 1;
@ -380,8 +361,7 @@ static void add_instrumentation(void) {
later on.
*/
//处理函数标签和条件分支。
//如果当前行是条件分支指令(如 jnz则插入插桩代码
if (skip_intel || skip_app || skip_csect || !instr_ok ||
line[0] == '#' || line[0] == ' ') continue;
@ -411,8 +391,7 @@ static void add_instrumentation(void) {
#ifdef __APPLE__
/* Apple: L<whatever><digit>: */
//处理标签。
//如果当前行是标签(如 .L0: 或 LBB0_0:),则根据需要设置 instrument_next表示后续需要插桩
if ((colon_pos = strstr(line, ":"))) {
if (line[0] == 'L' && isdigit(*(colon_pos - 1))) {
@ -471,11 +450,10 @@ static void add_instrumentation(void) {
}
}
//在所有插桩完成后,插入主插桩代码
if (ins_lines)
fputs(use_64bit ? main_payload_64 : main_payload_32, outf);
//关闭文件并输出插桩结果。
//如果没有插桩目标,则输出警告;否则输出插桩的详细信息
if (input_file) fclose(inf);
fclose(outf);
@ -495,30 +473,25 @@ static void add_instrumentation(void) {
/* Main entry point */
//程序的主入口点。处理命令行参数设置随机种子调用edit_params来编辑参数
//根据环境变量AFL_INST_RATIO设置instrumentation概率
//然后调用add_instrumentation来添加instrumentation代码并最终执行as
int main(int argc, char** argv) {
s32 pid; //用于存储 fork 后的子进程 ID
u32 rand_seed; //用于存储随机种子
int status; //用于存储子进程的退出状态
u8* inst_ratio_str = getenv("AFL_INST_RATIO"); //从环境变量 AFL_INST_RATIO 中获取插桩比例
s32 pid;
u32 rand_seed;
int status;
u8* inst_ratio_str = getenv("AFL_INST_RATIO");
struct timeval tv;
struct timezone tz;
//检查是否处于 clang 模式。
//如果环境变量 CLANG_ENV_VAR 存在,则设置 clang_mode 为 1否则为 0
clang_mode = !!getenv(CLANG_ENV_VAR);
//检查是否在终端运行,并输出提示信息。
//如果标准错误输出是终端且未设置 AFL_QUIET则输出程序名称和版本信息否则设置 be_quiet 为 1表示静默模式
if (isatty(2) && !getenv("AFL_QUIET")) {
SAYF(cCYA "afl-as " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
} else be_quiet = 1;
//检查命令行参数是否足够。
//如果参数少于 2 个,则输出帮助信息并退出程序
if (argc < 2) {
SAYF("\n"
@ -533,27 +506,22 @@ int main(int argc, char** argv) {
exit(1);
}
//生成并设置随机种子。
//使用当前时间、微秒数和进程 ID 生成随机种子,并调用 srandom 设置随机数生成器
gettimeofday(&tv, &tz);
rand_seed = tv.tv_sec ^ tv.tv_usec ^ getpid();
srandom(rand_seed);
//调用 edit_params 函数处理命令行参数。
//该函数会解析命令行参数并设置相关变量(如 input_file、modified_file 等)
edit_params(argc, argv);
//从环境变量 AFL_INST_RATIO 中读取插桩比例。
//如果插桩比例无效(不在 0 到 100 之间),则输出错误信息并终止程序
if (inst_ratio_str) {
if (sscanf(inst_ratio_str, "%u", &inst_ratio) != 1 || inst_ratio > 100)
FATAL("Bad value of AFL_INST_RATIO (must be between 0 and 100)");
}
//检查并设置环境变量 AS_LOOP_ENV_VAR。
//如果该环境变量已存在,则输出错误信息并终止程序(防止无限循环)。
//否则设置该环境变量为 "1"
if (getenv(AS_LOOP_ENV_VAR))
FATAL("Endless loop when calling 'as' (remove '.' from your PATH)");
@ -562,34 +530,27 @@ int main(int argc, char** argv) {
/* When compiling with ASAN, we don't have a particularly elegant way to skip
ASAN-specific branches. But we can probabilistically compensate for
that... */
//处理 ASAN 或 MSAN 模式。
//如果启用了 ASAN 或 MSAN则设置 sanitizer 为 1并将插桩比例除以 3
if (getenv("AFL_USE_ASAN") || getenv("AFL_USE_MSAN")) {
sanitizer = 1;
inst_ratio /= 3;
}
//调用 add_instrumentation 函数进行插桩。
//如果未设置 just_version则执行插桩操作
if (!just_version) add_instrumentation();
//创建子进程并执行 as 命令。
//使用 fork 创建子进程,并在子进程中调用 execvp 执行 as 命令。
//如果执行失败,则输出错误信息并终止程序
if (!(pid = fork())) {
execvp(as_params[0], (char**)as_params);//传递给as的参数数组
execvp(as_params[0], (char**)as_params);
FATAL("Oops, failed to execute '%s' - check your PATH", as_params[0]);
}
//等待子进程结束。
//如果 fork 失败,则输出错误信息并终止程序。
//使用 waitpid 等待子进程结束,并获取其退出状态
if (pid < 0) PFATAL("fork() failed");
if (waitpid(pid, &status, 0) <= 0) PFATAL("waitpid() failed");
//删除临时文件。
//如果未设置 AFL_KEEP_ASSEMBLY则删除生成的临时文件
if (!getenv("AFL_KEEP_ASSEMBLY")) unlink(modified_file);
//使用 WEXITSTATUS 获取子进程的退出状态,并将其作为程序的返回值
exit(WEXITSTATUS(status));
}

@ -111,7 +111,7 @@
oddball register it may touch.
*/
//32位系统的_trampoline_格式用于在函数调用前后保存和恢复寄存器状态并调用__afl_maybe_log函数
static const u8* trampoline_fmt_32 =
"\n"
@ -135,7 +135,6 @@ static const u8* trampoline_fmt_32 =
"/* --- END --- */\n"
"\n";
//64位系统的_trampoline_格式功能与32位版本类似
static const u8* trampoline_fmt_64 =
"\n"
@ -157,7 +156,6 @@ static const u8* trampoline_fmt_64 =
"/* --- END --- */\n"
"\n";
//这些是32位和64位系统的主要payload包含了AFL的instrumentation逻辑
static const u8* main_payload_32 =
"\n"
@ -169,7 +167,7 @@ static const u8* main_payload_32 =
".align 8\n"
"\n"
"__afl_maybe_log:\n" //检查共享内存区域是否已经映射并调用__afl_store来记录分支信息
"__afl_maybe_log:\n"
"\n"
" lahf\n"
" seto %al\n"
@ -180,7 +178,7 @@ static const u8* main_payload_32 =
" testl %edx, %edx\n"
" je __afl_setup\n"
"\n"
"__afl_store:\n" //计算并存储指定代码位置的分支信息
"__afl_store:\n"
"\n"
" /* Calculate and store hit for the code location specified in ecx. There\n"
" is a double-XOR way of doing this without tainting another register,\n"
@ -201,7 +199,7 @@ static const u8* main_payload_32 =
" incb (%edx, %edi, 1)\n"
#endif /* ^SKIP_COUNTS */
"\n"
"__afl_return:\n" //处理返回前的标志寄存器
"__afl_return:\n"
"\n"
" addb $127, %al\n"
" sahf\n"
@ -209,7 +207,7 @@ static const u8* main_payload_32 =
"\n"
".align 8\n"
"\n"
"__afl_setup:\n" //设置共享内存区域如果设置失败则跳转到__afl_setup_abort
"__afl_setup:\n"
"\n"
" /* Do not retry setup if we had previous failures. */\n"
"\n"
@ -251,7 +249,7 @@ static const u8* main_payload_32 =
" popl %ecx\n"
" popl %eax\n"
"\n"
"__afl_forkserver:\n" //进入fork服务器模式避免execve()调用的开销
"__afl_forkserver:\n"
"\n"
" /* Enter the fork server mode to avoid the overhead of execve() calls. */\n"
"\n"
@ -271,9 +269,9 @@ static const u8* main_payload_32 =
" addl $12, %esp\n"
"\n"
" cmpl $4, %eax\n"
" jne __afl_fork_resume\n" //在子进程中关闭文件描述符并继续执行
" jne __afl_fork_resume\n"
"\n"
"__afl_fork_wait_loop:\n" //等待父进程的指令如果读取失败则跳转到__afl_die
"__afl_fork_wait_loop:\n"
"\n"
" /* Wait for parent by reading from the pipe. Abort if read fails. */\n"
"\n"
@ -284,7 +282,7 @@ static const u8* main_payload_32 =
" addl $12, %esp\n"
"\n"
" cmpl $4, %eax\n"
" jne __afl_die\n" //终止当前进程
" jne __afl_die\n"
"\n"
" /* Once woken up, create a clone of our process. This is an excellent use\n"
" case for syscall(__NR_clone, 0, CLONE_PARENT), but glibc boneheadedly\n"
@ -348,7 +346,7 @@ static const u8* main_payload_32 =
" xorl %eax, %eax\n"
" call _exit\n"
"\n"
"__afl_setup_abort:\n" //记录设置失败避免重复调用shmget()/shmat()
"__afl_setup_abort:\n"
"\n"
" /* Record setup failure so that we don't keep calling\n"
" shmget() / shmat() over and over again. */\n"
@ -358,7 +356,7 @@ static const u8* main_payload_32 =
" popl %eax\n"
" jmp __afl_return\n"
"\n"
".AFL_VARS:\n" //定义了一系列全局变量包括共享内存区域指针、设置失败标志、前一个位置指针、fork的PID和临时变量
".AFL_VARS:\n"
"\n"
" .comm __afl_area_ptr, 4, 32\n"
" .comm __afl_setup_failure, 1, 32\n"
@ -368,7 +366,7 @@ static const u8* main_payload_32 =
" .comm __afl_fork_pid, 4, 32\n"
" .comm __afl_temp, 4, 32\n"
"\n"
".AFL_SHM_ENV:\n" //定义了共享内存环境变量的名称
".AFL_SHM_ENV:\n"
" .asciz \"" SHM_ENV_VAR "\"\n"
"\n"
"/* --- END --- */\n"
@ -381,7 +379,7 @@ static const u8* main_payload_32 =
they are doing relocations differently from everybody else. We also need
to work around the crash issue with .lcomm and the fact that they don't
recognize .string. */
//由于这些平台在指令识别和库函数调用方面与其他平台不同,因此需要特定的代码来处理这些差异
#ifdef __APPLE__
# define CALL_L64(str) "call _" str "\n"
#else

File diff suppressed because it is too large Load Diff

@ -67,8 +67,7 @@ static u8 be_quiet, /* Quiet mode */
/* Try to find our "fake" GNU assembler in AFL_PATH or at the location derived
from argv[0]. If that fails, abort. */
/* 67-120行: find_as()函数
AFL""GNU */
static void find_as(u8* argv0) {
// 这个函数尝试在AFL_PATH环境变量指定的路径或从argv[0]派生的路径中找到AFL的“假”GNU汇编器。如果找不到程序将终止。
u8 *afl_path = getenv("AFL_PATH");
@ -99,7 +98,7 @@ static void find_as(u8* argv0) {
*slash = '/';
tmp = alloc_printf("%s/afl-as", dir);
// 检查默认位置
if (!access(tmp, X_OK)) {
as_path = dir;
ck_free(tmp);
@ -122,8 +121,7 @@ static void find_as(u8* argv0) {
/* Copy argv to cc_params, making the necessary edits. */
/* 122-213行: edit_params()函数
*/
static void edit_params(u32 argc, char** argv) {
//定义了一个函数edit_params它接受两个参数argc是参数的数量argv是参数的数组。
@ -133,12 +131,12 @@ static void edit_params(u32 argc, char** argv) {
#if defined(__FreeBSD__) && defined(__x86_64__)
u8 m32_set = 0;
#endif
// 分配参数数组空间
cc_params = ck_alloc((argc + 128) * sizeof(u8*));//分配内存以存储修改后的参数列表大小为argc + 128个u8*类型的指针。
// 处理编译器名称
name = strrchr(argv[0], '/');//找到argv[0](程序的路径)中最后一个'/'字符,这通常用于获取程序的名称。
if (!name) name = argv[0]; else name++;//如果name为NULL即argv[0]中没有'/'则name指向argv[0]的开始。否则name向前移动一个字符跳过'/'。
// 设置Clang模式
if (!strncmp(name, "afl-clang", 9)) {
clang_mode = 1;//检查程序名称是否以"afl-clang"开头如果是设置clang_mode标志为1
@ -194,7 +192,7 @@ static void edit_params(u32 argc, char** argv) {
#endif /* __APPLE__ */
}
// 处理各种编译标志
while (--argc) {
u8* cur = *(++argv);
@ -271,7 +269,7 @@ static void edit_params(u32 argc, char** argv) {
}
// 处理ASAN/MSAN配置
if (!getenv("AFL_DONT_OPTIMIZE")) {//检查是否设置了环境变量AFL_DONT_OPTIMIZE。
#if defined(__FreeBSD__) && defined(__x86_64__)
@ -294,7 +292,7 @@ static void edit_params(u32 argc, char** argv) {
/* Two indicators that you're building for fuzzing; one of them is
AFL-specific, the other is shared with libfuzzer. */
// 添加AFL的as路径
cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1";
cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1";
//向参数列表中添加两个宏定义,这些宏定义指示编译器代码将用于模糊测试。
@ -327,7 +325,7 @@ int main(int argc, char** argv) {
SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
} else be_quiet = 1;
// 参数检查
if (argc < 2) {
SAYF("\n"

@ -1,29 +1,35 @@
/*
2015 Google LLC
/*
Copyright 2015 Google LLC All rights reserved.
Apache License, 2.0
使
Licensed under the Apache License, Version 2.0 (the "License");
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
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.
*/
/*
- CPU
/*
american fuzzy lop - free CPU gizmo
-----------------------------------
Michal Zalewski <lcamtuf@google.com>
Written and maintained by Michal Zalewski <lcamtuf@google.com>
CPU
afl-fuzz UIdocs/parallel_fuzzing.txt
This tool provides a fairly accurate measurement of CPU preemption rate.
It is meant to complement the quick-and-dirty load average widget shown
in the afl-fuzz UI. See docs/parallel_fuzzing.txt for more info.
CPUI/O100% CPU
For some work loads, the tool may actually suggest running more instances
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.
getrusage()Jakub Wilk
The idea for the getrusage()-based approach comes from Jakub Wilk.
*/
#define AFL_MAIN
@ -49,151 +55,119 @@
#endif /* __linux__ */
/*
Unix
*/
/* Get unix time in microseconds. */
static u64 get_cur_time_us(void) {
// 定义一个timeval结构体变量tv用于存储当前时间
struct timeval tv;
// 定义一个timezone结构体变量tz用于存储时区信息
struct timezone tz;
// 使用gettimeofday函数获取当前时间并存储到tv和tz中
gettimeofday(&tv, &tz);
// 将秒转换为微秒,并加上微秒部分,返回当前时间的微秒值
return (tv.tv_sec * 1000000ULL) + tv.tv_usec;
}
/*
CPU使
*/
/* Get CPU usage in microseconds. */
static u64 get_cpu_usage_us(void) {
// 定义一个rusage结构体变量u用于存储资源使用情况
struct rusage u;
// 使用getrusage函数获取当前进程的资源使用情况并存储到u中
getrusage(RUSAGE_SELF, &u);
// 计算用户态和核心态的总使用时间(微秒),并返回
return (u.ru_utime.tv_sec * 1000000ULL) + u.ru_utime.tv_usec +
(u.ru_stime.tv_sec * 1000000ULL) + u.ru_stime.tv_usec;
}
/*
*/
/* Measure preemption rate. */
static u32 measure_preemption(u32 target_ms) {
// 定义两个易失性变量,用于循环测试
static volatile u32 v1, v2;
// 定义变量用于存储开始和结束的时间微秒以及CPU使用时间
u64 st_t, en_t, st_c, en_c, real_delta, slice_delta;
// 定义循环重复次数
s32 loop_repeats = 0;
// 获取当前时间(微秒)
st_t = get_cur_time_us();
// 获取当前CPU使用时间微秒
st_c = get_cpu_usage_us();
repeat_loop:
// 设置v1为一个忙循环的计数
v1 = CTEST_BUSY_CYCLES;
// 执行忙循环同时v2自增
while (v1--) v2++;
// 调用sched_yield()让出CPU
sched_yield();
// 再次获取当前时间(微秒)
en_t = get_cur_time_us();
// 如果当前时间与开始时间的差小于目标时间(毫秒转换为微秒),则增加循环次数并继续循环
if (en_t - st_t < target_ms * 1000) {
loop_repeats++;
goto repeat_loop;
}
/* 让我们看看这段时间里我们实际上有多少百分比的机会运行,
*/
/* Let's see what percentage of this time we actually had a chance to
run, and how much time was spent in the penalty box. */
// 获取当前CPU使用时间微秒
en_c = get_cpu_usage_us();
// 计算实际时间差毫秒和CPU使用时间差毫秒
real_delta = (en_t - st_t) / 1000;
slice_delta = (en_c - st_c) / 1000;
// 返回实际时间差占CPU使用时间差的百分比即抢占率
return real_delta * 100 / slice_delta;
}
/*
*/
/* Do the benchmark thing. */
int main(int argc, char** argv) {
#ifdef HAVE_AFFINITY
// 获取在线的CPU核心数
u32 cpu_cnt = sysconf(_SC_NPROCESSORS_ONLN),
// 初始化空闲CPU和可能可用CPU的计数器
idle_cpus = 0, maybe_cpus = 0, i;
// 打印欢迎信息和版本号
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
// 打印测量每个核心抢占率所需的时间
ACTF("Measuring per-core preemption rate (this will take %0.02f sec)...",
((double)CTEST_CORE_TRG_MS) / 1000);
// 遍历每个CPU核心
for (i = 0; i < cpu_cnt; i++) {
// 创建子进程
s32 fr = fork();
// 如果fork失败则打印错误信息并退出
if (fr < 0) PFATAL("fork failed");
// 如果是子进程,则执行以下操作
if (!fr) {
// 定义CPU集合
cpu_set_t c;
// 定义CPU使用率百分比
u32 util_perc;
// 初始化CPU集合
CPU_ZERO(&c);
// 将当前核心加入CPU集合
CPU_SET(i, &c);
// 如果设置CPU亲和性失败则打印错误信息并退出
if (sched_setaffinity(0, sizeof(c), &c))
PFATAL("sched_setaffinity failed for cpu %d", i);
// 测量抢占率
util_perc = measure_preemption(CTEST_CORE_TRG_MS);
// 如果使用率低于110%,则标记为核心可用,并退出
if (util_perc < 110) {
SAYF(" Core #%u: " cLGN "AVAILABLE" cRST "(%u%%)\n", i, util_perc);
SAYF(" Core #%u: " cLGN "AVAILABLE " cRST "(%u%%)\n", i, util_perc);
exit(0);
} else if (util_perc < 250) {
// 如果使用率低于250%,则标记为核心需要谨慎,并退出
SAYF(" Core #%u: " cYEL "CAUTION" cRST "(%u%%)\n", i, util_perc);
SAYF(" Core #%u: " cYEL "CAUTION " cRST "(%u%%)\n", i, util_perc);
exit(1);
}
// 如果使用率高于250%,则标记为核心过载,并退出
SAYF(" Core #%u: " cLRD "OVERBOOKED" cRST "(%u%%)\n" cRST, i,
SAYF(" Core #%u: " cLRD "OVERBOOKED " cRST "(%u%%)\n" cRST, i,
util_perc);
exit(2);
@ -201,35 +175,44 @@ int main(int argc, char** argv) {
}
// 等待子进程结束,并统计空闲和可能可用的核心数
for (i = 0; i < cpu_cnt; i++) {
int ret;
if (waitpid(-1, &ret, 0) < 0) PFATAL("waitpid failed");
if (WEXITSTATUS(ret) == 0) idle_cpus++;
if (WEXITSTATUS(ret) <= 1) maybe_cpus++;
}
// 根据空闲和可能可用的核心数打印结果
SAYF(cGRA "\n>>> ");
if (idle_cpus) {
if (maybe_cpus == idle_cpus) {
SAYF(cLGN "PASS: " cRST "You can run more processes on %u core%s.",
idle_cpus, idle_cpus > 1 ? "s" : "");
} else {
SAYF(cLGN "PASS: " cRST "You can run more processes on %u to %u core%s.",
idle_cpus, maybe_cpus, maybe_cpus > 1 ? "s" : "");
}
SAYF(cGRA " <<<" cRST "\n\n");
return 0;
}
if (maybe_cpus) {
SAYF(cYEL "CAUTION: " cRST "You may still have %u core%s available.",
maybe_cpus, maybe_cpus > 1 ? "s" : "");
SAYF(cGRA " <<<" cRST "\n\n");
return 1;
}
SAYF(cLRD "FAIL: " cRST "All cores are overbooked.");
@ -238,35 +221,38 @@ int main(int argc, char** argv) {
#else
// 如果没有CPU亲和性支持则执行总体抢占率的测量
u32 util_perc;
SAYF(cCYA "afl-gotcpu " cBRI VERSION cRST " by <lcamtuf@google.com>\n");
/* 运行一个忙循环持续CTEST_TARGET_MS毫秒。 */
/* Run a busy loop for CTEST_TARGET_MS. */
ACTF("Measuring gross preemption rate (this will take %0.02f sec)...",
((double)CTEST_TARGET_MS) / 1000);
// 测量抢占率
util_perc = measure_preemption(CTEST_TARGET_MS);
/* 输出最终结果。 */
/* Deliver the final verdict. */
SAYF(cGRA "\n>>> ");
if (util_perc < 105) {
SAYF(cLGN "PASS: " cRST "You can probably run additional processes.");
} else if (util_perc < 130) {
SAYF(cYEL "CAUTION: " cRST "Your CPU may be somewhat overbooked (%u%%).",
util_perc);
} else {
SAYF(cLRD "FAIL: " cRST "Your CPU is overbooked (%u%%).", util_perc);
}
SAYF(cGRA " <<<" cRST "\n\n");
// 返回结果代码
return (util_perc > 105) + (util_perc > 130);
#endif /* ^HAVE_AFFINITY */

@ -54,31 +54,31 @@
#include <sys/types.h>
#include <sys/resource.h>
static s32 child_pid; /* 被测试程序的进程 PID */
static s32 child_pid; /* PID of the tested program */
static u8* trace_bits; /* 用于存储插桩信息的共享内存 */
static u8* trace_bits; /* SHM with instrumentation bitmap */
static u8 *out_file, /* 输出文件路径 */
*doc_path, /* 文档目录路径 */
*target_path, /* 被测目标程序的路径 */
*at_file; /* `@@` 的替换字符串,用于输入文件 */
static u8 *out_file, /* Trace output file */
*doc_path, /* Path to docs */
*target_path, /* Path to target binary */
*at_file; /* Substitution string for @@ */
static u32 exec_tmout; /* 测试程序的执行超时时间 (毫秒) */
static u32 exec_tmout; /* Exec timeout (ms) */
static u64 mem_limit = MEM_LIMIT; /* 测试程序的内存限制 (MB) */
static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */
static s32 shm_id; /* 共享内存 (SHM) 的标识符 */
static s32 shm_id; /* ID of the SHM region */
static u8 quiet_mode, /* 是否隐藏非必要的信息 */
edges_only, /* 是否只关心边覆盖率 */
cmin_mode, /* 是否以 `afl-cmin` 模式生成输出 */
binary_mode, /* 是否以二进制方式输出插桩信息 */
keep_cores; /* A是否允许生成 core dump 文件 */
static u8 quiet_mode, /* Hide non-essential messages? */
edges_only, /* Ignore hit counts? */
cmin_mode, /* Generate output in afl-cmin mode? */
binary_mode, /* Write output as a binary map */
keep_cores; /* Allow coredumps? */
static volatile u8
stop_soon, /* 是否按下了 Ctrl-C (中止标志) */
child_timed_out, /* 子进程是否超时 */
child_crashed; /* 子进程是否崩溃 */
stop_soon, /* Ctrl-C pressed? */
child_timed_out, /* Child timed out? */
child_crashed; /* Child crashed? */
/* Classify tuple counts. Instead of mapping to individual bits, as in
afl-fuzz.c, we map to more user-friendly numbers between 1 and 8. */
@ -95,7 +95,7 @@ static const u8 count_class_human[256] = {
[32 ... 127] = 7,
[128 ... 255] = 8
}; // 映射插桩命中计数到 1-8 的更具可读性的区间。0 代表未命中1-3 被保留为原值;其余区间依次分段映射,便于统计分析。
};
static const u8 count_class_binary[256] = {
@ -109,24 +109,23 @@ static const u8 count_class_binary[256] = {
[32 ... 127] = 64,
[128 ... 255] = 128
}; // 将插桩计数按指数方式映射到二进制值,便于程序进一步处理。
};
static void classify_counts(u8* mem, const u8* map) {//对插桩命中计数 (mem) 进行分类处理,基于全局变量 edges_only 的值选择分类模式。
//u8* mem插桩数据的内存指针。const u8* map分类映射表
static void classify_counts(u8* mem, const u8* map) {
u32 i = MAP_SIZE;
if (edges_only) {
while (i--) {
if (*mem) *mem = 1;// 只关心边的覆盖情况,命中计数直接归一
if (*mem) *mem = 1;
mem++;
}
} else {
while (i--) {
*mem = map[*mem];// 使用提供的映射表对命中计数进行分类
*mem = map[*mem];
mem++;
}
@ -135,105 +134,95 @@ static void classify_counts(u8* mem, const u8* map) {//对插桩命中计数 (me
}
/* 删除共享内存 (atexit 处理程序). */
/* Get rid of shared memory (atexit handler). */
static void remove_shm(void) {//释放分配的共享内存。
static void remove_shm(void) {
shmctl(shm_id, IPC_RMID, NULL); //使用 shmctl 函数删除共享内存区域
//IPC_RMID 标志用于标记共享内存为 "移除",在无其他进程访问后会自动释放。
//通过 atexit 注册,在程序退出时自动调用,确保清理资源。
shmctl(shm_id, IPC_RMID, NULL);
}
/* 配置共享内存 */
/* Configure shared memory. */
static void setup_shm(void) {// 创建共享内存,分配给 trace_bits 用于记录插桩数据。通过 atexit 注册清理函数,确保资源的释放。
static void setup_shm(void) {
u8* shm_str;
/* 创建一个私有的共享内存区域,大小为 MAP_SIZE */
shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
if (shm_id < 0) PFATAL("shmget() failed");/* 创建失败时输出错误并退出 */
if (shm_id < 0) PFATAL("shmget() failed");
atexit(remove_shm);/* 注册 atexit 处理程序以在程序退出时删除共享内存 */
atexit(remove_shm);
/* 将共享内存 ID 转换为字符串并设置环境变量 */
shm_str = alloc_printf("%d", shm_id);
setenv(SHM_ENV_VAR, shm_str, 1);
ck_free(shm_str);/* 释放临时字符串内存 */
ck_free(shm_str);
/* 将共享内存附加到当前进程的地址空间 */
trace_bits = shmat(shm_id, NULL, 0);
if (trace_bits == (void *)-1) PFATAL("shmat() failed"); /* 附加失败时退出 */
if (trace_bits == (void *)-1) PFATAL("shmat() failed");
}
<<<<<<< HEAD
/* 写入测试结果 */
=======
/* 配置共享内存。 */
>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a
/* Write results. */
static u32 write_results(void) { //将插桩结果输出到文件,支持二进制和文本模式。
static u32 write_results(void) {
s32 fd; /* 文件描述符 */
u32 i, ret = 0; /* 计数器和返回值 */
s32 fd;
u32 i, ret = 0;
u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"), /* 仅输出崩溃路径? */
caa = !!getenv("AFL_CMIN_ALLOW_ANY"); /* 是否允许所有路径? */
u8 cco = !!getenv("AFL_CMIN_CRASHES_ONLY"),
caa = !!getenv("AFL_CMIN_ALLOW_ANY");
/* 根据输出文件路径的类型处理 */
if (!strncmp(out_file, "/dev/", 5)) { /* 如果是设备文件路径 */
if (!strncmp(out_file, "/dev/", 5)) {
fd = open(out_file, O_WRONLY, 0600);
if (fd < 0) PFATAL("Unable to open '%s'", out_file);
} else if (!strcmp(out_file, "-")) { /* 如果是标准输出 */
} else if (!strcmp(out_file, "-")) {
fd = dup(1); /* 复制标准输出描述符 */
fd = dup(1);
if (fd < 0) PFATAL("Unable to open stdout");
} else { /* 普通文件路径 */
} else {
unlink(out_file); /* 删除已有的同名文件 (忽略错误) */
unlink(out_file); /* Ignore errors */
fd = open(out_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (fd < 0) PFATAL("Unable to create '%s'", out_file);
}
/* 如果以二进制模式输出结果 */
if (binary_mode) {
for (i = 0; i < MAP_SIZE; i++)
if (trace_bits[i]) ret++;/* 统计非零插桩计数 */
if (trace_bits[i]) ret++;
ck_write(fd, trace_bits, MAP_SIZE, out_file);/* 将插桩数据写入文件 */
ck_write(fd, trace_bits, MAP_SIZE, out_file);
close(fd);
} else { /* 文本模式输出 */
} else {
FILE* f = fdopen(fd, "w");
if (!f) PFATAL("fdopen() failed");
/* 遍历插桩数据,按文本格式输出 */
for (i = 0; i < MAP_SIZE; i++) {
if (!trace_bits[i]) continue;/* 跳过空条目 */
if (!trace_bits[i]) continue;
ret++;
if (cmin_mode) {/* afl-cmin 模式 */
if (cmin_mode) {
if (child_timed_out) break; /* 如果子进程超时,停止输出 */
if (!caa && child_crashed != cco) break;/* 根据环境变量条件过滤 */
if (child_timed_out) break;
if (!caa && child_crashed != cco) break;
fprintf(f, "%u%u\n", trace_bits[i], i);
} else fprintf(f, "%06u:%u\n", i, trace_bits[i]);/* 标准格式 */
} else fprintf(f, "%06u:%u\n", i, trace_bits[i]);
}
@ -246,54 +235,42 @@ static u32 write_results(void) { //将插桩结果输出到文件,支持二进
}
<<<<<<< HEAD
/* 处理超时信号 */
=======
/* 处理超时信号。 */
>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a
/* Handle timeout signal. */
static void handle_timeout(int sig) {
child_timed_out = 1; /* 设置子进程超时标志 */
if (child_pid > 0) kill(child_pid, SIGKILL); /* 强制杀死子进程 */
child_timed_out = 1;
if (child_pid > 0) kill(child_pid, SIGKILL);
}
<<<<<<< HEAD
/* 执行目标程序 */
/* Execute target application. */
=======
/* 执行目标应用程序。 */
/* 运行目标程序并等待其结束。 */
/* 处理超时和信号。 */
>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a
static void run_target(char** argv) {
static struct itimerval it;/* 定时器结构 */
static struct itimerval it;
int status = 0;
if (!quiet_mode)
SAYF("-- Program output begins --\n" cRST);/* 输出调试信息 */
SAYF("-- Program output begins --\n" cRST);
MEM_BARRIER(); /* 防止编译器重排内存操作 */
MEM_BARRIER();
child_pid = fork();/* 创建子进程 */
child_pid = fork();
if (child_pid < 0) PFATAL("fork() failed");/* 如果 fork 失败,退出程序 */
if (child_pid < 0) PFATAL("fork() failed");
if (!child_pid) { /* 子进程逻辑 */
if (!child_pid) {
struct rlimit r;
if (quiet_mode) { /* 如果静默模式开启,将子进程输出重定向到 `/dev/null` */
if (quiet_mode) {
s32 fd = open("/dev/null", O_RDWR);
if (fd < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0) {
*(u32*)trace_bits = EXEC_FAIL_SIG; /* 设置错误标志 */
*(u32*)trace_bits = EXEC_FAIL_SIG;
PFATAL("Descriptor initialization failed");
}
@ -301,35 +278,35 @@ static void run_target(char** argv) {
}
if (mem_limit) {/* 设置子进程内存限制 */
if (mem_limit) {
r.rlim_max = r.rlim_cur = ((rlim_t)mem_limit) << 20;
#ifdef RLIMIT_AS
setrlimit(RLIMIT_AS, &r); /* 设置内存限制 (按地址空间限制),忽略错误 */
setrlimit(RLIMIT_AS, &r); /* Ignore errors */
#else
setrlimit(RLIMIT_DATA, &r); /* 设置内存限制 (按数据段大小限制),忽略错误 */
setrlimit(RLIMIT_DATA, &r); /* Ignore errors */
#endif /* ^RLIMIT_AS */
}
// 根据平台使用适当的资源限制类型 (RLIMIT_AS 或 RLIMIT_DATA) 限制子进程的内存使用。
if (!keep_cores) r.rlim_max = r.rlim_cur = 0; /* 禁止 core dump */
// 控制是否允许子进程生成 core dump 文件。
else r.rlim_max = r.rlim_cur = RLIM_INFINITY; /* 允许无限 core dump */
setrlimit(RLIMIT_CORE, &r); /* 设置 core dump 限制,忽略错误 */
if (!keep_cores) r.rlim_max = r.rlim_cur = 0;
else r.rlim_max = r.rlim_cur = RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &r); /* Ignore errors */
if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0); // 强制目标程序加载动态库时立即绑定符号,避免延迟加载可能引发的问题。
if (!getenv("LD_BIND_LAZY")) setenv("LD_BIND_NOW", "1", 0);
setsid(); /* 创建一个新会话,隔离子进程 */
setsid();
execv(target_path, argv);/* 替换当前进程镜像为目标程序 */
execv(target_path, argv);
*(u32*)trace_bits = EXEC_FAIL_SIG; /* 如果 execv 返回,则标记执行失败 */
exit(0);/* 终止子进程 */
*(u32*)trace_bits = EXEC_FAIL_SIG;
exit(0);
}
@ -343,9 +320,9 @@ static void run_target(char** argv) {
}
setitimer(ITIMER_REAL, &it, NULL);/* 配置超时定时器 */
setitimer(ITIMER_REAL, &it, NULL);
if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed"); /* 等待子进程结束 */
if (waitpid(child_pid, &status, 0) <= 0) FATAL("waitpid() failed");
child_pid = 0;
it.it_value.tv_sec = 0;
@ -361,7 +338,7 @@ static void run_target(char** argv) {
classify_counts(trace_bits, binary_mode ?
count_class_binary : count_class_human);
/* 将 trace_bits 中的插桩命中数据分类为二进制或人类可读格式 */
if (!quiet_mode)
SAYF(cRST "-- Program output ends --\n");
@ -385,11 +362,11 @@ static void run_target(char** argv) {
/* Handle Ctrl-C and the like. */
static void handle_stop_sig(int sig) {//捕获停止信号 (SIGHUP, SIGINT, SIGTERM) 并设置全局停止标志。终止子进程以确保程序退出。
static void handle_stop_sig(int sig) {
stop_soon = 1;/* 设置标志,通知主程序停止 */
stop_soon = 1;
if (child_pid > 0) kill(child_pid, SIGKILL);/* 强制杀死子进程 */
if (child_pid > 0) kill(child_pid, SIGKILL);
}
@ -397,7 +374,7 @@ static void handle_stop_sig(int sig) {//捕获停止信号 (SIGHUP, SIGINT, SIGT
/* Do basic preparations - persistent fds, filenames, etc. */
static void set_up_environment(void) {
// 设置环境变量,配置 AddressSanitizer (ASAN) 和 MemorySanitizer (MSAN) 行为:禁用内存泄漏检测 (detect_leaks=0)。禁用符号化输出 (symbolize=0)。配置错误退出行为 (abort_on_error=1)。
setenv("ASAN_OPTIONS", "abort_on_error=1:"
"detect_leaks=0:"
"symbolize=0:"
@ -408,7 +385,7 @@ static void set_up_environment(void) {
"abort_on_error=1:"
"allocator_may_return_null=1:"
"msan_track_origins=0", 0);
// 如果存在 AFL_PRELOAD 环境变量,将其设置为动态库预加载路径。
if (getenv("AFL_PRELOAD")) {
setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1);
setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1);
@ -430,34 +407,32 @@ static void setup_signal_handlers(void) {
sigemptyset(&sa.sa_mask);
/* Various ways of saying "stop". */
// 为停止信号 (SIGHUP, SIGINT, SIGTERM) 注册处理函数 handle_stop_sig。
sa.sa_handler = handle_stop_sig; /* 设置停止信号的处理函数 */
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;/* 设置超时信号的处理函数 */
sa.sa_handler = handle_timeout;
sigaction(SIGALRM, &sa, NULL);
}
/* 检测@@在参数中。 */
/* 检测并替换@@参数。 */
/* 用于处理传递给目标程序的文件路径参数。 */
/* Detect @@ in args. */
static void detect_file_args(char** argv) {
// 获取当前工作目录,用于构造绝对路径。
u32 i = 0;
u8* cwd = getcwd(NULL, 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) {
@ -467,13 +442,13 @@ static void detect_file_args(char** argv) {
/* Be sure that we're always using fully-qualified paths. */
if (at_file[0] == '/') aa_subst = at_file;/* 如果是绝对路径,直接使用 */
else aa_subst = alloc_printf("%s/%s", cwd, at_file);/* 构造绝对路径 */
if (at_file[0] == '/') aa_subst = at_file;
else aa_subst = alloc_printf("%s/%s", cwd, at_file);
/* Construct a replacement argv value. */
*aa_loc = 0;
n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);/* 替换 @@ */
n_arg = alloc_printf("%s%s%s", argv[i], aa_subst, aa_loc + 2);
argv[i] = n_arg;
*aa_loc = '@';
@ -491,8 +466,6 @@ static void detect_file_args(char** argv) {
/* Show banner. */
// 打印工具的名称和版本信息。
static void show_banner(void) {
@ -500,12 +473,12 @@ static void show_banner(void) {
}
/* 显示使用提示。 */
/* Display usage hints. */
static void usage(u8* argv0) {
show_banner();
// 显示工具的用法说明,包括参数和选项说明。
SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n"
"Required parameters:\n\n"
@ -519,7 +492,7 @@ static void usage(u8* argv0) {
" -Q - use binary-only instrumentation (QEMU mode)\n\n"
"Other settings:\n\n"
// 描述可用的执行控制选项,包括超时时间和内存限制。
" -q - sink program's output and don't show messages\n"
" -e - show edge coverage only, ignore hit counts\n"
" -c - allow core dumps\n"
@ -535,23 +508,17 @@ static void usage(u8* argv0) {
}
<<<<<<< HEAD
/* Find binary. */
// 找到目标程序的可执行文件路径 (target_path)。
=======
/* 查找二进制文件。 */
/* 查找并验证二进制文件。 */
/* 如果二进制文件不存在或不可执行,则打印错误信息并退出。 */
>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a
static void find_binary(u8* fname) {
// fname目标程序的文件名。如果 fname 包含斜杠(表明是路径)或没有设置 PATH 环境变量,则直接检查 fname 是否是一个可执行文件。否则,遍历 PATH 环境变量中定义的路径,尝试找到 fname。
u8* env_path = 0;
struct stat st;
if (strchr(fname, '/') || !(env_path = getenv("PATH"))) {
// 如果 fname 是路径(包含斜杠),直接检查其合法性。
target_path = ck_strdup(fname);
// 如果 PATH 环境变量未设置,同样直接检查 fname。
if (stat(target_path, &st) || !S_ISREG(st.st_mode) ||
!(st.st_mode & 0111) || st.st_size < 4)
FATAL("Program '%s' not found or not executable", fname);
@ -571,14 +538,14 @@ static void find_binary(u8* fname) {
} else cur_elem = ck_strdup(env_path);
env_path = delim;
// 按冒号分隔符解析 PATH 环境变量,依次检查每个路径是否包含目标文件。
if (cur_elem[0])
target_path = alloc_printf("%s/%s", cur_elem, fname);
else
target_path = ck_strdup(fname);
ck_free(cur_elem);
// 将文件名与路径组合成完整路径。检查文件是否存在、是否是普通文件、是否可执行且文件大小合理。如果找到符合条件的文件路径,则退出循环。
if (!stat(target_path, &st) && S_ISREG(st.st_mode) &&
(st.st_mode & 0111) && st.st_size >= 4) break;
@ -586,7 +553,7 @@ static void find_binary(u8* fname) {
target_path = 0;
}
// 如果遍历完所有路径后仍未找到目标文件,则程序退出。
if (!target_path) FATAL("Program '%s' not found or not executable", fname);
}
@ -594,33 +561,24 @@ static void find_binary(u8* fname) {
}
<<<<<<< HEAD
/* Fix up argv for QEMU. */
// 为在 QEMU 模式下执行目标程序,调整参数列表 argv。
=======
/*修正argv以用于QEMU*/
/* 为QEMU模式修正参数*/
/* 返回新的参数数组*/
>>>>>>> 31b9bf56f4cc249d8b6bb414203bff28d8cf724a
static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
// own_loc当前程序的路径用于寻找 afl-qemu-trace。argv原始参数列表。argc参数个数。
char** new_argv = ck_alloc(sizeof(char*) * (argc + 4));
u8 *tmp, *cp, *rsl, *own_copy;
/* 设置 QEMU 的环境变量 QEMU_LOG禁用链式日志。 */
/* Workaround for a QEMU stability glitch. */
setenv("QEMU_LOG", "nochain", 1);
memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc);
// 为新参数列表分配空间。
// 将原始参数 argv 复制到新参数列表的合适位置。
// 添加必要的 QEMU 参数 -- 和 target_path。
new_argv[2] = target_path;
new_argv[1] = "--";
/* Now we need to actually find qemu for argv[0]. */
// 检查 AFL_PATH 环境变量是否指向 afl-qemu-trace并验证其是否可执行。
tmp = getenv("AFL_PATH");
if (tmp) {
@ -634,7 +592,7 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
return new_argv;
}
// 使用当前程序的路径 own_loc 尝试找到 afl-qemu-trace。
own_copy = ck_strdup(own_loc);
rsl = strrchr(own_copy, '/');
@ -653,48 +611,40 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) {
}
} else ck_free(own_copy);
// 检查预定义路径 BIN_PATH 下是否存在 afl-qemu-trace。
if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) {
target_path = new_argv[0] = BIN_PATH "/afl-qemu-trace";
return new_argv;
}
// 如果未找到 afl-qemu-trace程序退出并报错。
FATAL("Unable to find 'afl-qemu-trace'.");
}
/* Main entry point */
/*解析命令行参数并执行目标程序 */
/*根据参数执行不同的操作 */
int main(int argc, char** argv) {
//opt用于存储当前解析的命令行选项。
//mem_limit_given标志是否设置了内存限制选项。
//timeout_given标志是否设置了超时选项。
//qemu_mode标志是否启用了 QEMU 模式。
//tcnt存储记录的插桩数据元组计数。
//use_argv用于执行目标程序的参数列表。
s32 opt;
u8 mem_limit_given = 0, timeout_given = 0, qemu_mode = 0;
u32 tcnt;
char** use_argv;
// 检查 DOC_PATH 是否存在,如果不存在则回退到默认路径 "docs"。
doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;
// 使用 getopt 解析命令行选项,支持的选项包括:
while ((opt = getopt(argc,argv,"+o:m:t:A:eqZQbcV")) > 0)
switch (opt) {
// -o指定输出文件。
case 'o':
if (out_file) FATAL("Multiple -o options not supported");
out_file = optarg;
break;
// -m设置内存限制。
case 'm': {
u8 suffix = 'M';
@ -731,7 +681,7 @@ int main(int argc, char** argv) {
}
break;
// -t设置超时时间。
case 't':
if (timeout_given) FATAL("Multiple -t options not supported");
@ -746,19 +696,19 @@ int main(int argc, char** argv) {
}
break;
// -e只关注边覆盖率。
case 'e':
if (edges_only) FATAL("Multiple -e options not supported");
edges_only = 1;
break;
// -q静默模式不显示多余信息。
case 'q':
if (quiet_mode) FATAL("Multiple -q options not supported");
quiet_mode = 1;
break;
// -Z启用 afl-cmin 模式,内部使用,设置 cmin_mode 和 quiet_mode。
case 'Z':
/* This is an undocumented option to write data in the syntax expected
@ -767,13 +717,13 @@ int main(int argc, char** argv) {
cmin_mode = 1;
quiet_mode = 1;
break;
// -A设置 @@ 替换文件路径。
case 'A':
/* Another afl-cmin specific feature. */
at_file = optarg;
break;
// -Q启用 QEMU 模式,如果未指定内存限制,使用默认值 MEM_LIMIT_QEMU。
case 'Q':
if (qemu_mode) FATAL("Multiple -Q options not supported");
@ -781,7 +731,7 @@ int main(int argc, char** argv) {
qemu_mode = 1;
break;
// -b启用二进制输出模式设置全局变量 binary_mode = 1。
case 'b':
/* Secret undocumented mode. Writes output in raw binary format
@ -789,13 +739,13 @@ int main(int argc, char** argv) {
binary_mode = 1;
break;
// -c允许生成核心转储文件设置全局变量 keep_cores = 1。
case 'c':
if (keep_cores) FATAL("Multiple -c options not supported");
keep_cores = 1;
break;
// -V显示版本号并退出程序。
case 'V':
show_banner();
@ -807,40 +757,39 @@ int main(int argc, char** argv) {
}
if (optind == argc || !out_file) usage(argv[0]); //如果未指定目标程序路径或输出文件,则显示用法说明并退出。
setup_shm();// 调用 setup_shm 函数,配置用于插桩数据存储的共享内存。
setup_signal_handlers();// 注册信号处理函数处理程序停止SIGINT、SIGTERM或超时SIGALRM等事件。
if (optind == argc || !out_file) usage(argv[0]);
set_up_environment();// 配置环境变量,例如 ASAN_OPTIONS 和 MSAN_OPTIONS。
setup_shm();
setup_signal_handlers();
find_binary(argv[optind]);//使用 find_binary 函数查找目标程序的可执行文件路径,并存储到全局变量 target_path。
set_up_environment();
find_binary(argv[optind]);
if (!quiet_mode) {// 如果未启用静默模式,显示工具信息和目标程序路径。
if (!quiet_mode) {
show_banner();
ACTF("Executing '%s'...\n", target_path);
}
detect_file_args(argv + optind); //检测命令行参数中的 @@ 并将其替换为指定的文件路径。
detect_file_args(argv + optind);
if (qemu_mode) //如果启用了 QEMU 模式,调用 get_qemu_argv 函数为 QEMU 准备参数列表。
if (qemu_mode)
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
else // 否则直接使用原始参数。
else
use_argv = argv + optind;
run_target(use_argv);
tcnt = write_results(); // 调用 write_results 函数,将插桩数据写入指定的输出文件。
tcnt = write_results();
if (!quiet_mode) {
if (!tcnt) FATAL("No instrumentation detected" cRST); //如果没有捕获插桩数据,显示错误并退出。
OKF("Captured %u tuples in '%s'." cRST, tcnt, out_file); // 否则显示捕获的元组数量和输出文件路径。
if (!tcnt) FATAL("No instrumentation detected" cRST);
OKF("Captured %u tuples in '%s'." cRST, tcnt, out_file);
}
exit(child_crashed * 2 + child_timed_out);
// 根据子进程状态退出.如果子进程崩溃:返回 2。如果子进程超时返回 1。正常退出返回 0。
}

File diff suppressed because it is too large Load Diff

@ -36,7 +36,8 @@
#include "types.h"
#include "debug.h"
/* 提供给用户使用的宏用于将sprintf()的输出到一个动态分配的缓冲区。 */
/* User-facing macro to sprintf() to a dynamically allocated buffer. */
#define alloc_printf(_str...) ({ \
u8* _tmp; \
s32 _len = snprintf(NULL, 0, _str); \
@ -46,24 +47,29 @@
_tmp; \
})
/* 宏,用于强制执行分配限制,作为防止整数溢出的最后一道防线。 */
/* Macro to enforce allocation limits as a last-resort defense against
integer overflows. */
#define ALLOC_CHECK_SIZE(_s) do { \
if ((_s) > MAX_ALLOC) \
ABORT("Bad alloc request: %u bytes", (_s)); \
} while (0)
/* 宏用于检查malloc()失败等情况。 */
/* Macro to check malloc() failures and the like. */
#define ALLOC_CHECK_RESULT(_r, _s) do { \
if (!(_r)) \
ABORT("Out of memory: can't allocate %u bytes", (_s)); \
} while (0)
/* 用于标记已使用/已释放块的魔术标记。 */
#define ALLOC_MAGIC_C1 0xFF00FF00 /* 已使用头部(双字) */
#define ALLOC_MAGIC_F 0xFE00FE00 /* 已释放头部(双字) */
#define ALLOC_MAGIC_C2 0xF0 /* 已使用尾部(字节) */
/* Magic tokens used to mark used / freed chunks. */
#define ALLOC_MAGIC_C1 0xFF00FF00 /* Used head (dword) */
#define ALLOC_MAGIC_F 0xFE00FE00 /* Freed head (dword) */
#define ALLOC_MAGIC_C2 0xF0 /* Used tail (byte) */
/* Positions of guard tokens in relation to the user-visible pointer. */
/* 与用户可见指针相关的保护标记的位置。 */
#define ALLOC_C1(_ptr) (((u32*)(_ptr))[-2])
#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1])
#define ALLOC_C2(_ptr) (((u8*)(_ptr))[ALLOC_S(_ptr)])
@ -71,13 +77,15 @@
#define ALLOC_OFF_HEAD 8
#define ALLOC_OFF_TOTAL (ALLOC_OFF_HEAD + 1)
/* ck_realloc_block()的分配器增量。 */
/* Allocator increments for ck_realloc_block(). */
#define ALLOC_BLK_INC 256
/* 用于指针的合理性检查宏。 */
/* Sanity-checking macros for pointers. */
#define CHECK_PTR(_p) do { \
if (_p) { \
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) { \
if (ALLOC_C1(_p) ^ ALLOC_MAGIC_C1) {\
if (ALLOC_C1(_p) == ALLOC_MAGIC_F) \
ABORT("Use after free."); \
else ABORT("Corrupted head alloc canary."); \
@ -93,206 +101,246 @@
_tmp; \
})
/* 分配一个缓冲区明确不将其清零。对于零大小的请求返回NULL。 */
/* Allocate a buffer, explicitly not zeroing it. Returns NULL for zero-sized
requests. */
static inline void* DFL_ck_alloc_nozero(u32 size) {
void* ret;
void* ret;
if (!size) return NULL;
if (!size) return NULL;
ALLOC_CHECK_SIZE(size);
ret = malloc(size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
ALLOC_CHECK_SIZE(size);
ret = malloc(size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
ret += ALLOC_OFF_HEAD;
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
return ret;
return ret;
}
/* 分配一个缓冲区,返回清零后的内存。 */
/* Allocate a buffer, returning zeroed memory. */
static inline void* DFL_ck_alloc(u32 size) {
void* mem;
void* mem;
if (!size) return NULL;
mem = DFL_ck_alloc_nozero(size);
if (!size) return NULL;
mem = DFL_ck_alloc_nozero(size);
return memset(mem, 0, size);
return memset(mem, 0, size);
}
/* 释放内存检查是否重复释放和堆损坏。当DEBUG_BUILD被设置时
0xFF */
/* Free memory, checking for double free and corrupted heap. When DEBUG_BUILD
is set, the old memory will be also clobbered with 0xFF. */
static inline void DFL_ck_free(void* mem) {
if (!mem) return; /* 如果指针为空,不执行任何操作 */
CHECK_PTR(mem); /* 检查是否重复释放和堆损坏 */
if (!mem) return;
CHECK_PTR(mem);
#ifdef DEBUG_BUILD
/* Catch pointer issues sooner. */
memset(mem, 0xFF, ALLOC_S(mem));
#ifdef DEBUG_BUILD /* 如果是调试构建用0xFF覆盖内存 */
/* 尽早捕捉指针问题。 */
memset(mem, 0xFF, ALLOC_S(mem)); /* 用0xFF覆盖内存 */
#endif /* DEBUG_BUILD */
ALLOC_C1(mem) = ALLOC_MAGIC_F; /* 标记内存为已释放 */
ALLOC_C1(mem) = ALLOC_MAGIC_F;
free(mem - ALLOC_OFF_HEAD);
free(mem - ALLOC_OFF_HEAD); /* 释放内存,调整头偏移 */
}
/* 重新分配缓冲区,检查问题并清零任何新添加的尾部。
DEBUG_BUILD0xFF */
/* Re-allocate a buffer, checking for issues and zeroing any newly-added tail.
With DEBUG_BUILD, the buffer is always reallocated to a new addresses and the
old memory is clobbered with 0xFF. */
static inline void* DFL_ck_realloc(void* orig, u32 size) {
void* ret;
u32 old_size = 0;
if (!size) {
DFL_ck_free(orig); /* 如果新大小为0释放原始内存 */
return NULL;
}
void* ret;
u32 old_size = 0;
if (!size) {
DFL_ck_free(orig);
return NULL;
}
if (orig) {
if (orig) {
CHECK_PTR(orig); /* 检查是否重复释放和堆损坏 */
CHECK_PTR(orig);
#ifndef DEBUG_BUILD /* 在非调试构建中,可以重用内存 */
ALLOC_C1(orig) = ALLOC_MAGIC_F; /* 标记内存为已释放 */
#ifndef DEBUG_BUILD
ALLOC_C1(orig) = ALLOC_MAGIC_F;
#endif /* !DEBUG_BUILD */
old_size = ALLOC_S(orig); /* 获取原始大小 */
orig -= ALLOC_OFF_HEAD; /* 调整指针以适应头部偏移 */
old_size = ALLOC_S(orig);
orig -= ALLOC_OFF_HEAD;
ALLOC_CHECK_SIZE(old_size); /* 检查原始大小是否有效 */
}
ALLOC_CHECK_SIZE(old_size);
}
ALLOC_CHECK_SIZE(size); /* 检查新大小是否有效 */
ALLOC_CHECK_SIZE(size);
#ifndef DEBUG_BUILD /* 在非调试构建中,尝试原地调整内存大小 */
ret = realloc(orig, size + ALLOC_OFF_TOTAL); /* 重新分配内存 */
ALLOC_CHECK_RESULT(ret, size); /* 检查重新分配是否成功 */
#else /* 在调试构建中,总是分配新内存并清除旧内存 */
/* 尽早捕捉指针问题:强制重新定位,并确保旧缓冲区被清除。 */
#ifndef DEBUG_BUILD
ret = malloc(size + ALLOC_OFF_TOTAL); /* 分配新内存 */
ALLOC_CHECK_RESULT(ret, size); /* 检查分配是否成功 */
ret = realloc(orig, size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
#else
/* Catch pointer issues sooner: force relocation and make sure that the
original buffer is wiped. */
ret = malloc(size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
if (orig) {
memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size));
memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size);
ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F;
free(orig);
}
if (orig) {
memcpy(ret + ALLOC_OFF_HEAD, orig + ALLOC_OFF_HEAD, MIN(size, old_size)); /* 将数据复制到新内存 */
memset(orig + ALLOC_OFF_HEAD, 0xFF, old_size); /* 用0xFF覆盖旧内存 */
ALLOC_C1(orig + ALLOC_OFF_HEAD) = ALLOC_MAGIC_F; /* 标记旧内存为已释放 */
free(orig); /* 释放旧内存 */
}
#endif /* ^!DEBUG_BUILD */
ret += ALLOC_OFF_HEAD; /* 调整指针以适应头部偏移 */
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1; /* 标记内存为已使用 */
ALLOC_S(ret) = size; /* 存储内存块的大小 */
ALLOC_C2(ret) = ALLOC_MAGIC_C2; /* 标记内存块的结尾 */
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
if (size > old_size) /* 如果新大小更大,清零新的尾部 */
memset(ret + old_size, 0, size - old_size);
if (size > old_size)
memset(ret + old_size, 0, size - old_size);
return ret;
return ret; /* 返回(可能新的)内存块 */
}
/* 以ALLOC_BLK_INC的增量重新分配缓冲区用于加速重复的小realloc操作而不需要复杂化用户代码。 */
/* Re-allocate a buffer with ALLOC_BLK_INC increments (used to speed up
repeated small reallocs without complicating the user code). */
static inline void* DFL_ck_realloc_block(void* orig, u32 size) {
#ifndef DEBUG_BUILD
if (orig) {
if (orig) {
CHECK_PTR(orig); /* 检查原始指针是否有效 */
CHECK_PTR(orig);
if (ALLOC_S(orig) >= size) return orig; /* 如果当前大小已满足需求则不进行realloc */
if (ALLOC_S(orig) >= size) return orig;
size += ALLOC_BLK_INC; /* 增加ALLOC_BLK_INC以减少频繁的小realloc操作 */
size += ALLOC_BLK_INC;
}
}
#endif /* !DEBUG_BUILD */
return DFL_ck_realloc(orig, size); /* 调用DFL_ck_realloc进行实际的realloc操作 */
return DFL_ck_realloc(orig, size);
}
/* 创建一个包含字符串副本的缓冲区。对于NULL输入返回NULL。 */
/* Create a buffer with a copy of a string. Returns NULL for NULL inputs. */
static inline u8* DFL_ck_strdup(u8* str) {
void* ret;
u32 size;
void* ret;
u32 size;
if (!str) return NULL; /* 如果输入字符串为空则返回NULL */
if (!str) return NULL;
size = strlen((char*)str) + 1; /* 计算字符串长度加1为'\0'留空间) */
size = strlen((char*)str) + 1;
ALLOC_CHECK_SIZE(size); /* 检查分配大小是否超出限制 */
ret = malloc(size + ALLOC_OFF_TOTAL); /* 分配内存 */
ALLOC_CHECK_RESULT(ret, size); /* 检查内存分配是否成功 */
ALLOC_CHECK_SIZE(size);
ret = malloc(size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
ret += ALLOC_OFF_HEAD; /* 调整指针以适应头部偏移 */
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1; /* 设置头部魔术标记 */
ALLOC_S(ret) = size; /* 存储分配的大小 */
ALLOC_C2(ret) = ALLOC_MAGIC_C2; /* 设置尾部魔术标记 */
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
return memcpy(ret, str, size); /* 复制字符串并返回新指针 */
return memcpy(ret, str, size);
}
/* 创建一个包含内存块副本的缓冲区。对于零大小或NULL输入返回NULL。 */
static inline void* DFL_ck_memdup(void* mem, u32 size) {
void* ret;
/* Create a buffer with a copy of a memory block. Returns NULL for zero-sized
or NULL inputs. */
static inline void* DFL_ck_memdup(void* mem, u32 size) {
if (!mem || !size) return NULL; /* 如果内存块为空或大小为零则返回NULL */
void* ret;
ALLOC_CHECK_SIZE(size); /* 检查分配大小是否超出限制 */
ret = malloc(size + ALLOC_OFF_TOTAL); /* 分配内存 */
ALLOC_CHECK_RESULT(ret, size); /* 检查内存分配是否成功 */
if (!mem || !size) return NULL;
ret += ALLOC_OFF_HEAD; /* 调整指针以适应头部偏移 */
ALLOC_CHECK_SIZE(size);
ret = malloc(size + ALLOC_OFF_TOTAL);
ALLOC_CHECK_RESULT(ret, size);
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1; /* 设置头部魔术标记 */
ALLOC_S(ret) = size; /* 存储分配的大小 */
ALLOC_C2(ret) = ALLOC_MAGIC_C2; /* 设置尾部魔术标记 */
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
return memcpy(ret, mem, size); /* 复制内存块并返回新指针 */
return memcpy(ret, mem, size);
}
/* 创建一个包含文本块的缓冲区并在末尾追加NUL终止符。对于零大小或NULL输入返回NULL。 */
static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
u8* ret;
/* Create a buffer with a block of text, appending a NUL terminator at the end.
Returns NULL for zero-sized or NULL inputs. */
static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
if (!mem || !size) return NULL; /* 如果内存块为空或大小为零则返回NULL */
u8* ret;
ALLOC_CHECK_SIZE(size); /* 检查分配大小是否超出限制 */
ret = malloc(size + ALLOC_OFF_TOTAL + 1); /* 分配内存,额外+1用于NUL终止符 */
ALLOC_CHECK_RESULT(ret, size); /* 检查内存分配是否成功 */
if (!mem || !size) return NULL;
ret += ALLOC_OFF_HEAD; /* 调整指针以适应头部偏移 */
ALLOC_CHECK_SIZE(size);
ret = malloc(size + ALLOC_OFF_TOTAL + 1);
ALLOC_CHECK_RESULT(ret, size);
ret += ALLOC_OFF_HEAD;
ALLOC_C1(ret) = ALLOC_MAGIC_C1; /* 设置头部魔术标记 */
ALLOC_S(ret) = size; /* 存储分配的大小 */
ALLOC_C2(ret) = ALLOC_MAGIC_C2; /* 设置尾部魔术标记 */
ALLOC_C1(ret) = ALLOC_MAGIC_C1;
ALLOC_S(ret) = size;
ALLOC_C2(ret) = ALLOC_MAGIC_C2;
memcpy(ret, mem, size); /* 复制内存块 */
ret[size] = 0; /* 设置NUL终止符 */
memcpy(ret, mem, size);
ret[size] = 0;
return ret; /* 返回新指针 */
return ret;
}
#ifndef DEBUG_BUILD
/* 在非调试模式下我们直接将上述函数别名为用户可见的名称如ck_alloc()。 */
/* In non-debug mode, we just do straightforward aliasing of the above functions
to user-visible names such as ck_alloc(). */
#define ck_alloc DFL_ck_alloc
#define ck_alloc_nozero DFL_ck_alloc_nozero
@ -307,191 +355,223 @@ static inline u8* DFL_ck_memdup_str(u8* mem, u32 size) {
#else
/* 在调试模式下,我们还跟踪内存分配以检测内存泄漏,这个过程会经历更多的间接层。 */
/* In debugging mode, we also track allocations to detect memory leaks, and the
flow goes through one more layer of indirection. */
/* 分配跟踪数据结构: */
/* Alloc tracking data structures: */
#define ALLOC_BUCKETS 4096 /* 定义分配桶的数量 */
#define ALLOC_BUCKETS 4096
struct TRK_obj {
void* ptr; /* 指向分配的内存块 */
char* file, * func; /* 分配时的文件名和函数名 */
u32 line; /* 分配时的代码行号 */
void *ptr;
char *file, *func;
u32 line;
};
#ifdef AFL_MAIN
struct TRK_obj* TRK[ALLOC_BUCKETS]; /* 跟踪分配的内存对象数组 */
u32 TRK_cnt[ALLOC_BUCKETS]; /* 每个桶中跟踪对象的数量 */
struct TRK_obj* TRK[ALLOC_BUCKETS];
u32 TRK_cnt[ALLOC_BUCKETS];
# define alloc_report() TRK_report() /* 定义alloc_report宏为TRK_report函数 */
# define alloc_report() TRK_report()
#else
extern struct TRK_obj* TRK[ALLOC_BUCKETS]; /* 外部声明跟踪分配的内存对象数组 */
extern u32 TRK_cnt[ALLOC_BUCKETS]; /* 外部声明每个桶中跟踪对象的数量 */
extern struct TRK_obj* TRK[ALLOC_BUCKETS];
extern u32 TRK_cnt[ALLOC_BUCKETS];
# define alloc_report() /* 在非AFL_MAIN环境下alloc_report宏为空 */
# define alloc_report()
#endif /* ^AFL_MAIN */
/* 为给定指针分配桶的函数: */
/* Bucket-assigning function for a given pointer: */
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS)
#define TRKH(_ptr) (((((u32)(_ptr)) >> 16) ^ ((u32)(_ptr))) % ALLOC_BUCKETS) /* 定义桶分配宏 */
/* 将新分配的内存对象添加到跟踪列表中。 */
/* Add a new entry to the list of allocated objects. */
static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func,
u32 line) {
u32 i, bucket;
u32 line) {
if (!ptr) return; /* 如果指针为空,则返回 */
u32 i, bucket;
bucket = TRKH(ptr); /* 获取桶编号 */
if (!ptr) return;
/* 在该桶的条目列表中找到一个空闲位置。 */
bucket = TRKH(ptr);
for (i = 0; i < TRK_cnt[bucket]; i++)
if (!TRK[bucket][i].ptr) { /* 如果找到空闲位置 */
TRK[bucket][i].ptr = ptr;
TRK[bucket][i].file = (char*)file;
TRK[bucket][i].func = (char*)func;
TRK[bucket][i].line = line;
return;
}
/* Find a free slot in the list of entries for that bucket. */
for (i = 0; i < TRK_cnt[bucket]; i++)
if (!TRK[bucket][i].ptr) {
TRK[bucket][i].ptr = ptr;
TRK[bucket][i].file = (char*)file;
TRK[bucket][i].func = (char*)func;
TRK[bucket][i].line = line;
return;
}
/* No space available - allocate more. */
/* 没有可用空间 - 分配更多空间。 */
TRK[bucket] = DFL_ck_realloc_block(TRK[bucket],
(TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj));
TRK[bucket] = DFL_ck_realloc_block(TRK[bucket],
(TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); /* 重新分配桶的大小 */
TRK[bucket][i].ptr = ptr;
TRK[bucket][i].file = (char*)file;
TRK[bucket][i].func = (char*)func;
TRK[bucket][i].line = line;
TRK[bucket][i].ptr = ptr;
TRK[bucket][i].file = (char*)file;
TRK[bucket][i].func = (char*)func;
TRK[bucket][i].line = line;
TRK_cnt[bucket]++;
TRK_cnt[bucket]++; /* 更新桶中条目的数量 */
}
/* 从分配的内存对象列表中移除条目。 */
/* Remove entry from the list of allocated objects. */
static inline void TRK_free_buf(void* ptr, const char* file, const char* func,
u32 line) {
u32 i, bucket;
u32 line) {
if (!ptr) return; /* 如果指针为空,则返回 */
u32 i, bucket;
bucket = TRKH(ptr); /* 获取桶编号 */
if (!ptr) return;
/* 在列表中找到该元素... */
bucket = TRKH(ptr);
for (i = 0; i < TRK_cnt[bucket]; i++)
if (TRK[bucket][i].ptr == ptr) { /* 如果找到匹配的指针 */
TRK[bucket][i].ptr = 0; /* 将指针设置为NULL */
return;
}
/* Find the element on the list... */
for (i = 0; i < TRK_cnt[bucket]; i++)
if (TRK[bucket][i].ptr == ptr) {
TRK[bucket][i].ptr = 0;
return;
}
WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)",
func, file, line);
WARNF("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)",
func, file, line); /* 警告:尝试释放未分配的内存 */
}
/* 对所有未释放的对象进行最终报告。 */
/* Do a final report on all non-deallocated objects. */
static inline void TRK_report(void) {
u32 i, bucket;
fflush(0); /* 清空输出缓冲区 */
u32 i, bucket;
fflush(0);
for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++)
for (i = 0; i < TRK_cnt[bucket]; i++)
if (TRK[bucket][i].ptr)
WARNF("ALLOC: Memory never freed, created in %s (%s:%u)",
TRK[bucket][i].func, TRK[bucket][i].file, TRK[bucket][i].line);
for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++)
for (i = 0; i < TRK_cnt[bucket]; i++)
if (TRK[bucket][i].ptr) /* 如果指针不为空 */
WARNF("ALLOC: Memory never freed, created in %s (%s:%u)",
TRK[bucket][i].func, TRK[bucket][i].file, TRK[bucket][i].line); /* 警告:内存从未释放 */
}
/* 非调试函数的简单包装器: */
/* Simple wrappers for non-debugging functions: */
static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func,
u32 line) {
void* ret = DFL_ck_alloc(size); /* 分配内存 */
TRK_alloc_buf(ret, file, func, line); /* 添加到跟踪列表 */
return ret;
u32 line) {
void* ret = DFL_ck_alloc(size);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
static inline void* TRK_ck_realloc(void* orig, u32 size, const char* file,
const char* func, u32 line) {
void* ret = DFL_ck_realloc(orig, size); /* 重新分配内存 */
TRK_free_buf(orig, file, func, line); /* 从跟踪列表中移除 */
TRK_alloc_buf(ret, file, func, line); /* 添加到跟踪列表 */
return ret;
const char* func, u32 line) {
void* ret = DFL_ck_realloc(orig, size);
TRK_free_buf(orig, file, func, line);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
/* 重新分配内存块,同时更新跟踪信息。 */
static inline void* TRK_ck_realloc_block(void* orig, u32 size, const char* file,
const char* func, u32 line) {
void* ret = DFL_ck_realloc_block(orig, size); /* 调用DFL_ck_realloc_block重新分配内存 */
TRK_free_buf(orig, file, func, line); /* 从跟踪列表中移除原始内存块 */
TRK_alloc_buf(ret, file, func, line); /* 将新内存块添加到跟踪列表 */
return ret; /* 返回新内存块 */
const char* func, u32 line) {
void* ret = DFL_ck_realloc_block(orig, size);
TRK_free_buf(orig, file, func, line);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
/* 复制字符串并分配内存,同时更新跟踪信息。 */
static inline void* TRK_ck_strdup(u8* str, const char* file, const char* func,
u32 line) {
void* ret = DFL_ck_strdup(str); /* 调用DFL_ck_strdup复制字符串 */
TRK_alloc_buf(ret, file, func, line); /* 将新内存块添加到跟踪列表 */
return ret; /* 返回新内存块 */
u32 line) {
void* ret = DFL_ck_strdup(str);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
/* 复制内存块并分配内存,同时更新跟踪信息。 */
static inline void* TRK_ck_memdup(void* mem, u32 size, const char* file,
const char* func, u32 line) {
void* ret = DFL_ck_memdup(mem, size); /* 调用DFL_ck_memdup复制内存块 */
TRK_alloc_buf(ret, file, func, line); /* 将新内存块添加到跟踪列表 */
return ret; /* 返回新内存块 */
const char* func, u32 line) {
void* ret = DFL_ck_memdup(mem, size);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
/* 复制内存块并添加字符串终止符,同时更新跟踪信息。 */
static inline void* TRK_ck_memdup_str(void* mem, u32 size, const char* file,
const char* func, u32 line) {
void* ret = DFL_ck_memdup_str(mem, size); /* 调用DFL_ck_memdup_str复制内存块并添加终止符 */
TRK_alloc_buf(ret, file, func, line); /* 将新内存块添加到跟踪列表 */
return ret; /* 返回新内存块 */
const char* func, u32 line) {
void* ret = DFL_ck_memdup_str(mem, size);
TRK_alloc_buf(ret, file, func, line);
return ret;
}
/* 释放内存并更新跟踪信息。 */
static inline void TRK_ck_free(void* ptr, const char* file,
const char* func, u32 line) {
TRK_free_buf(ptr, file, func, line); /* 从跟踪列表中移除内存块 */
DFL_ck_free(ptr); /* 释放内存块 */
const char* func, u32 line) {
TRK_free_buf(ptr, file, func, line);
DFL_ck_free(ptr);
}
/* 将用户可见的名称别名为跟踪函数: */
/* Aliasing user-facing names to tracking functions: */
#define ck_alloc(_p1) \
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__) /* 定义ck_alloc宏为TRK_ck_alloc */
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
#define ck_alloc_nozero(_p1) \
TRK_ck_alloc_nozero(_p1, __FILE__, __FUNCTION__, __LINE__) /* 定义ck_alloc_nozero宏为TRK_ck_alloc_nozero */
TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__)
#define ck_realloc(_p1, _p2) \
TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) /* 定义ck_realloc宏为TRK_ck_realloc */
TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
#define ck_realloc_block(_p1, _p2) \
TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) /* 定义ck_realloc_block宏为TRK_ck_realloc_block */
TRK_ck_realloc_block(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
#define ck_strdup(_p1) \
TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__) /* 定义ck_strdup宏为TRK_ck_strdup */
TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__)
#define ck_memdup(_p1, _p2) \
TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) /* 定义ck_memdup宏为TRK_ck_memdup */
TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
#define ck_memdup_str(_p1, _p2) \
TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) /* 定义ck_memdup_str宏为TRK_ck_memdup_str */
TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__)
#define ck_free(_p1) \
TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__) /* 定义ck_free宏为TRK_ck_free */
TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__)
#endif /* ^!DEBUG_BUILD */
#endif /* ! _HAVE_ALLOC_INL_H */

@ -1,88 +1,82 @@
// 如果是Android平台且尚未定义_ANDROID_ASHMEM_H则定义它
#ifdef __ANDROID__
#ifndef _ANDROID_ASHMEM_H
#define _ANDROID_ASHMEM_H
// 包含所需的头文件
#include <fcntl.h>
#include <linux/ashmem.h> // 包含ashmem相关的ioctl操作
#include <linux/ashmem.h>
#include <linux/shm.h>
#include <sys/ioctl.h>
#include <sys/mman.h> // 包含内存映射函数
#include <sys/mman.h>
// 如果Android API级别大于或等于26Android 8.0则使用Bionic的shm*函数
#if __ANDROID_API__ >= 26
#define shmat bionic_shmat
#define shmctl bionic_shmctl
#define shmdt bionic_shmdt
#define shmget bionic_shmget
#endif
#include <sys/shm.h> // 包含标准的共享内存函数
#include <sys/shm.h>
#undef shmat
#undef shmctl
#undef shmdt
#undef shmget // 取消对Bionic函数的重定义
#undef shmget
#include <stdio.h>
// 定义ashmem设备的路径
#define ASHMEM_DEVICE "/dev/ashmem"
// 定义shmctl函数的封装用于删除共享内存
static inline int shmctl(int __shmid, int __cmd, struct shmid_ds* __buf) {
int ret = 0;
if (__cmd == IPC_RMID) {
int length = ioctl(__shmid, ASHMEM_GET_SIZE, NULL);
struct ashmem_pin pin = { 0, length };
ret = ioctl(__shmid, ASHMEM_UNPIN, &pin);
close(__shmid);
}
return ret;
static inline int shmctl(int __shmid, int __cmd, struct shmid_ds *__buf) {
int ret = 0;
if (__cmd == IPC_RMID) {
int length = ioctl(__shmid, ASHMEM_GET_SIZE, NULL);
struct ashmem_pin pin = {0, length};
ret = ioctl(__shmid, ASHMEM_UNPIN, &pin);
close(__shmid);
}
return ret;
}
// 定义shmget函数的封装用于创建共享内存
static inline int shmget(key_t __key, size_t __size, int __shmflg) {
(void)__shmflg;
int fd, ret;
char ourkey[11];
(void) __shmflg;
int fd, ret;
char ourkey[11];
fd = open(ASHMEM_DEVICE, O_RDWR);
if (fd < 0)
return fd;
fd = open(ASHMEM_DEVICE, O_RDWR);
if (fd < 0)
return fd;
sprintf(ourkey, "%d", __key);
ret = ioctl(fd, ASHMEM_SET_NAME, ourkey);
if (ret < 0)
goto error;
sprintf(ourkey, "%d", __key);
ret = ioctl(fd, ASHMEM_SET_NAME, ourkey);
if (ret < 0)
goto error;
ret = ioctl(fd, ASHMEM_SET_SIZE, __size);
if (ret < 0)
goto error;
ret = ioctl(fd, ASHMEM_SET_SIZE, __size);
if (ret < 0)
goto error;
return fd;
return fd;
error:
close(fd);
return ret;
close(fd);
return ret;
}
// 定义shmat函数的封装用于将共享内存附加到进程地址空间
static inline void* shmat(int __shmid, const void* __shmaddr, int __shmflg) {
(void)__shmflg;
int size;
void* ptr;
static inline void *shmat(int __shmid, const void *__shmaddr, int __shmflg) {
(void) __shmflg;
int size;
void *ptr;
size = ioctl(__shmid, ASHMEM_GET_SIZE, NULL);
if (size < 0) {
return NULL;
}
size = ioctl(__shmid, ASHMEM_GET_SIZE, NULL);
if (size < 0) {
return NULL;
}
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, __shmid, 0);
if (ptr == MAP_FAILED) {
return NULL;
}
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, __shmid, 0);
if (ptr == MAP_FAILED) {
return NULL;
}
return ptr;
return ptr;
}
#endif /* !_ANDROID_ASHMEM_H */
#endif /* !__ANDROID__ */
#endif /* !__ANDROID__ */

@ -27,7 +27,8 @@
#include "types.h"
/* Version string: */
#define VERSION "2.57b" // 定义版本号字符串
#define VERSION "2.57b"
/******************************************************
* *
@ -35,196 +36,228 @@
* *
******************************************************/
/* Comment out to disable terminal colors (note that this makes afl-analyze
a lot less nice): */
#define USE_COLOR // 启用终端颜色
/* Comment out to disable terminal colors (note that this makes afl-analyze
a lot less nice): */
#define USE_COLOR
/* Comment out to disable fancy ANSI boxes and use poor man's 7-bit UI: */
#define FANCY_BOXES // 启用ANSI框绘制
/* Comment out to disable fancy ANSI boxes and use poor man's 7-bit UI: */
#define FANCY_BOXES
/* Default timeout for fuzzed code (milliseconds). This is the upper bound,
also used for detecting hangs; the actual value is auto-scaled: */
#define EXEC_TIMEOUT 1000 // 默认超时时间(毫秒)
/* Timeout rounding factor when auto-scaling (milliseconds): */
#define EXEC_TM_ROUND 20 // 自动缩放时的超时舍入因子(毫秒)
#define EXEC_TIMEOUT 1000
/* Timeout rounding factor when auto-scaling (milliseconds): */
#define EXEC_TM_ROUND 20
/* 64bit arch MACRO */
#if (defined (__x86_64__) || defined (__arm64__) || defined (__aarch64__))
#define WORD_SIZE_64 1 // 定义64位架构宏
#define WORD_SIZE_64 1
#endif
/* Default memory limit for child process (MB): */
#ifndef WORD_SIZE_64
# define MEM_LIMIT 25 // 非64位架构的默认内存限制MB
# define MEM_LIMIT 25
#else
# define MEM_LIMIT 50 // 64位架构的默认内存限制MB
# define MEM_LIMIT 50
#endif /* ^!WORD_SIZE_64 */
/* Default memory limit when running in QEMU mode (MB): */
#define MEM_LIMIT_QEMU 200 // QEMU模式下的默认内存限制MB
#define MEM_LIMIT_QEMU 200
/* Number of calibration cycles per every new test case (and for test
cases that show variable behavior): */
#define CAL_CYCLES 8 // 每个新测试用例的校准周期数
#define CAL_CYCLES_LONG 40 // 长校准周期数
/* Number of subsequent timeouts before abandoning an input file: */
#define TMOUT_LIMIT 250 // 超时次数限制
#define CAL_CYCLES 8
#define CAL_CYCLES_LONG 40
/* Number of subsequent timeouts before abandoning an input file: */
#define TMOUT_LIMIT 250
/* Maximum number of unique hangs or crashes to record: */
#define KEEP_UNIQUE_HANG 500 // 最大记录的唯一挂起数
#define KEEP_UNIQUE_CRASH 5000 // 最大记录的唯一崩溃数
#define KEEP_UNIQUE_HANG 500
#define KEEP_UNIQUE_CRASH 5000
/* Baseline number of random tweaks during a single 'havoc' stage: */
#define HAVOC_CYCLES 256 // 'havoc'阶段的随机调整基数
#define HAVOC_CYCLES_INIT 1024 // 'havoc'阶段的初始随机调整数
#define HAVOC_CYCLES 256
#define HAVOC_CYCLES_INIT 1024
/* Maximum multiplier for the above (should be a power of two, beware
of 32-bit int overflows): */
#define HAVOC_MAX_MULT 16 // 'havoc'阶段的最大乘数
/* Absolute minimum number of havoc cycles (after all adjustments): */
#define HAVOC_MIN 16 // 'havoc'阶段的绝对最小周期数
#define HAVOC_MAX_MULT 16
/* Absolute minimum number of havoc cycles (after all adjustments): */
#define HAVOC_MIN 16
/* Maximum stacking for havoc-stage tweaks. The actual value is calculated
like this:
like this:
n = random between 1 and HAVOC_STACK_POW2
stacking = 2^n
In other words, the default (n = 7) produces 2, 4, 8, 16, 32, 64, or
128 stacked tweaks: */
#define HAVOC_STACK_POW2 7 // 'havoc'阶段的最大堆叠指数
/* Caps on block sizes for cloning and deletion operations. Each of these
ranges has a 33% probability of getting picked, except for the first
two cycles where smaller blocks are favored: */
#define HAVOC_BLK_SMALL 32 // 小块大小限制
#define HAVOC_BLK_MEDIUM 128 // 中块大小限制
#define HAVOC_BLK_LARGE 1500 // 大块大小限制
#define HAVOC_STACK_POW2 7
/* Caps on block sizes for cloning and deletion operations. Each of these
ranges has a 33% probability of getting picked, except for the first
two cycles where smaller blocks are favored: */
#define HAVOC_BLK_SMALL 32
#define HAVOC_BLK_MEDIUM 128
#define HAVOC_BLK_LARGE 1500
/* Extra-large blocks, selected very rarely (<5% of the time): */
#define HAVOC_BLK_XL 32768 // 特大块大小限制
/* Extra-large blocks, selected very rarely (<5% of the time): */
#define HAVOC_BLK_XL 32768
/* Probabilities of skipping non-favored entries in the queue, expressed as
percentages: */
#define SKIP_TO_NEW_PROB 99 // 跳过非优先队列项的概率(有新的待处理优先项)
#define SKIP_NFAV_OLD_PROB 95 // 跳过非优先队列项的概率(没有新的优先项,当前项已测试)
#define SKIP_NFAV_NEW_PROB 75 // 跳过非优先队列项的概率(没有新的优先项,当前项未测试)
/* Splicing cycle count: */
#define SPLICE_CYCLES 15 // 拼接周期数
#define SKIP_TO_NEW_PROB 99 /* ...when there are new, pending favorites */
#define SKIP_NFAV_OLD_PROB 95 /* ...no new favs, cur entry already fuzzed */
#define SKIP_NFAV_NEW_PROB 75 /* ...no new favs, cur entry not fuzzed yet */
/* Splicing cycle count: */
#define SPLICE_CYCLES 15
/* Nominal per-splice havoc cycle length: */
#define SPLICE_HAVOC 32 // 每次拼接的'havoc'周期长度
#define SPLICE_HAVOC 32
/* Maximum offset for integer addition / subtraction stages: */
#define ARITH_MAX 35 // 整数加减阶段的最大偏移量
#define ARITH_MAX 35
/* Limits for the test case trimmer. The absolute minimum chunk size; and
the starting and ending divisors for chopping up the input file: */
#define TRIM_MIN_BYTES 4 // 测试用例修剪器的最小块大小
#define TRIM_START_STEPS 16 // 测试用例修剪器的起始除数
#define TRIM_END_STEPS 1024 // 测试用例修剪器的结束除数
/* Maximum size of input file, in bytes (keep under 100MB): */
#define MAX_FILE (1 * 1024 * 1024) // 输入文件的最大大小(字节)
#define TRIM_MIN_BYTES 4
#define TRIM_START_STEPS 16
#define TRIM_END_STEPS 1024
/* Maximum size of input file, in bytes (keep under 100MB): */
#define MAX_FILE (1 * 1024 * 1024)
/* The same, for the test case minimizer: */
#define TMIN_MAX_FILE (10 * 1024 * 1024) // 测试用例最小化器的最大文件大小
#define TMIN_MAX_FILE (10 * 1024 * 1024)
/* Block normalization steps for afl-tmin: */
#define TMIN_SET_MIN_SIZE 4 // afl-tmin的块归一化最小大小
#define TMIN_SET_STEPS 128 // afl-tmin的块归一化步数
#define TMIN_SET_MIN_SIZE 4
#define TMIN_SET_STEPS 128
/* Maximum dictionary token size (-x), in bytes: */
#define MAX_DICT_FILE 128 // 最大字典令牌大小(字节)
#define MAX_DICT_FILE 128
/* Length limits for auto-detected dictionary tokens: */
#define MIN_AUTO_EXTRA 3 // 自动检测的字典令牌的最小长度
#define MAX_AUTO_EXTRA 32 // 自动检测的字典令牌的最大长度
#define MIN_AUTO_EXTRA 3
#define MAX_AUTO_EXTRA 32
/* Maximum number of user-specified dictionary tokens to use in deterministic
steps; past this point, the "extras/user" step will be still carried out,
but with proportionally lower odds: */
#define MAX_DET_EXTRAS 200 // 最大用户指定字典令牌数
/* Maximum number of auto-extracted dictionary tokens to actually use in fuzzing
(first value), and to keep in memory as candidates. The latter should be much
higher than the former. */
#define USE_AUTO_EXTRAS 50 // 实际用于模糊测试的自动提取字典令牌数
#define MAX_AUTO_EXTRAS (USE_AUTO_EXTRAS * 10) // 内存中候选的自动提取字典令牌数
#define MAX_DET_EXTRAS 200
/* Maximum number of auto-extracted dictionary tokens to actually use in fuzzing
(first value), and to keep in memory as candidates. The latter should be much
higher than the former. */
#define USE_AUTO_EXTRAS 50
#define MAX_AUTO_EXTRAS (USE_AUTO_EXTRAS * 10)
/* Scaling factor for the effector map used to skip some of the more
expensive deterministic steps. The actual divisor is set to
2^EFF_MAP_SCALE2 bytes: */
/* Scaling factor for the effector map used to skip some of the more
expensive deterministic steps. The actual divisor is set to
2^EFF_MAP_SCALE2 bytes: */
#define EFF_MAP_SCALE2 3 // 效应器映射的缩放因子
#define EFF_MAP_SCALE2 3
/* Minimum input file length at which the effector logic kicks in: */
#define EFF_MIN_LEN 128 // 效应器逻辑触发的最小输入文件长度
/* Minimum input file length at which the effector logic kicks in: */
#define EFF_MIN_LEN 128
/* Maximum effector density past which everything is just fuzzed
unconditionally (%): */
#define EFF_MAX_PERC 90 // 最大效应器密度(%
/* UI refresh frequency (Hz): */
#define UI_TARGET_HZ 5 // UI刷新频率Hz
#define EFF_MAX_PERC 90
/* UI refresh frequency (Hz): */
#define UI_TARGET_HZ 5
/* Fuzzer stats file and plot update intervals (sec): */
#define STATS_UPDATE_SEC 60 // 模糊统计文件更新间隔(秒)
#define PLOT_UPDATE_SEC 5 // 模糊统计图表更新间隔(秒)
#define STATS_UPDATE_SEC 60
#define PLOT_UPDATE_SEC 5
/* Smoothing divisor for CPU load and exec speed stats (1 - no smoothing). */
#define AVG_SMOOTHING 16 // CPU负载和执行速度统计的平滑除数
#define AVG_SMOOTHING 16
/* Sync interval (every n havoc cycles): */
#define SYNC_INTERVAL 5 // 同步间隔每n个havoc周期
#define SYNC_INTERVAL 5
/* Output directory reuse grace period (minutes): */
#define OUTPUT_GRACE 25 // 输出目录重用宽限期(分钟)
#define OUTPUT_GRACE 25
/* Uncomment to use simple file names (id_NNNNNN): */
// #define SIMPLE_FILES // 取消注释以使用简单文件名
// #define SIMPLE_FILES
/* List of interesting values to use in fuzzing. */
// 定义一系列有趣的值,用于模糊测试
/* 定义一组有趣的8位值用于模糊测试包括边界值和常见缓冲区大小 */
#define INTERESTING_8 \
-128, /* 减1时溢出的有符号8位值 */ \
-1, /* 通用的有趣值 */ \
0, /* 零值,常用于测试 */ \
1, /* 通用的有趣值 */ \
16, /* 常用缓冲区大小的偏移量 */ \
32, /* 常用缓冲区大小的偏移量 */ \
64, /* 常用缓冲区大小的偏移量 */ \
100, /* 常用缓冲区大小的偏移量 */ \
127 /* 加1时溢出的有符号8位值 */
/* 定义一组有趣的16位值用于模糊测试包括边界值和常见缓冲区大小 */
-128, /* Overflow signed 8-bit when decremented */ \
-1, /* */ \
0, /* */ \
1, /* */ \
16, /* One-off with common buffer size */ \
32, /* One-off with common buffer size */ \
64, /* One-off with common buffer size */ \
100, /* One-off with common buffer size */ \
127 /* Overflow signed 8-bit when incremented */
#define INTERESTING_16 \
-32768, /* 减1时溢出的有符号16位值 */ \
-129, /* 溢出的有符号8位值 */ \
128, /* 溢出的有符号8位值 */ \
255, /* 增1时溢出的无符号8位值 */ \
256, /* 溢出的无符号8位值 */ \
512, /* 常用缓冲区大小的偏移量 */ \
1000, /* 常用缓冲区大小的偏移量 */ \
1024, /* 常用缓冲区大小的偏移量 */ \
4096, /* 常用缓冲区大小的偏移量 */ \
32767 /* 加1时溢出的有符号16位值 */
/* 定义一组有趣的32位值用于模糊测试包括边界值和大数值 */
-32768, /* Overflow signed 16-bit when decremented */ \
-129, /* Overflow signed 8-bit */ \
128, /* Overflow signed 8-bit */ \
255, /* Overflow unsig 8-bit when incremented */ \
256, /* Overflow unsig 8-bit */ \
512, /* One-off with common buffer size */ \
1000, /* One-off with common buffer size */ \
1024, /* One-off with common buffer size */ \
4096, /* One-off with common buffer size */ \
32767 /* Overflow signed 16-bit when incremented */
#define INTERESTING_32 \
-2147483648LL, /* 减1时溢出的有符号32位值 */ \
-100663046, /* 大的负数(与字节序无关) */ \
-32769, /* 溢出的有符号16位值 */ \
32768, /* 溢出的有符号16位值 */ \
65535, /* 增1时溢出的无符号16位值 */ \
65536, /* 溢出的无符号16位值 */ \
100663045, /* 大的正数(与字节序无关) */ \
2147483647 /* 加1时溢出的有符号32位值 */
-2147483648LL, /* Overflow signed 32-bit when decremented */ \
-100663046, /* Large negative number (endian-agnostic) */ \
-32769, /* Overflow signed 16-bit */ \
32768, /* Overflow signed 16-bit */ \
65535, /* Overflow unsig 16-bit when incremented */ \
65536, /* Overflow unsig 16 bit */ \
100663045, /* Large positive number (endian-agnostic) */ \
2147483647 /* Overflow signed 32-bit when incremented */
/***********************************************************
* *
@ -232,66 +265,98 @@
* *
***********************************************************/
/* 定义 libc 伪随机数生成器重新播种的调用计数间隔 */
/* Call count interval between reseeding the libc PRNG from /dev/urandom: */
#define RESEED_RNG 10000
/* 定义从 GCC 传递给 'as' 的最大行长度,并用于解析配置文件 */
/* Maximum line length passed from GCC to 'as' and used for parsing
configuration files: */
#define MAX_LINE 8192
/* 定义用于传递共享内存ID给被调用程序的环境变量 */
/* Environment variable used to pass SHM ID to the called program. */
#define SHM_ENV_VAR "__AFL_SHM_ID"
/* 定义其他不太有趣,仅内部使用的变量 */
/* Other less interesting, internal-only variables. */
#define CLANG_ENV_VAR "__AFL_CLANG_MODE"
#define AS_LOOP_ENV_VAR "__AFL_AS_LOOPCHECK"
#define PERSIST_ENV_VAR "__AFL_PERSISTENT"
#define DEFER_ENV_VAR "__AFL_DEFER_FORKSRV"
/* 定义代码中用于延迟和持久模式的签名 */
/* In-code signatures for deferred and persistent mode. */
#define PERSIST_SIG "##SIG_AFL_PERSISTENT##"
#define DEFER_SIG "##SIG_AFL_DEFER_FORKSRV##"
/* 定义用于表示执行失败的独特位图签名 */
/* Distinctive bitmap signature used to indicate failed execution: */
#define EXEC_FAIL_SIG 0xfee1dead
/* 定义用于表示MSAN内存sanitizer触发条件的独特退出代码 */
/* Distinctive exit code used to indicate MSAN trip condition: */
#define MSAN_ERROR 86
/* 定义用于fork服务器命令的指定文件描述符 */
/* Designated file descriptors for forkserver commands (the application will
use FORKSRV_FD and FORKSRV_FD + 1): */
#define FORKSRV_FD 198
/* 定义fork服务器初始化超时乘数 */
/* Fork server init timeout multiplier: we'll wait the user-selected
timeout plus this much for the fork server to spin up. */
#define FORK_WAIT_MULT 10
/* 定义校准超时调整,恢复模糊测试会话或校准已添加的内部发现时更加宽松 */
/* Calibration timeout adjustments, to be a bit more generous when resuming
fuzzing sessions or trying to calibrate already-added internal finds.
The first value is a percentage, the other is in milliseconds: */
#define CAL_TMOUT_PERC 125
#define CAL_TMOUT_ADD 50
/* 定义校准一个案例前放弃的机会数 */
/* Number of chances to calibrate a case before giving up: */
#define CAL_CHANCES 3
/* 定义跟踪二进制文件的映射大小 */
/* Map size for the traced binary (2^MAP_SIZE_POW2). Must be greater than
2; you probably want to keep it under 18 or so for performance reasons
(adjusting AFL_INST_RATIO when compiling is probably a better way to solve
problems with complex programs). You need to recompile the target binary
after changing this - otherwise, SEGVs may ensue. */
#define MAP_SIZE_POW2 16
#define MAP_SIZE (1 << MAP_SIZE_POW2)
/* 定义最大分配请求大小 */
/* Maximum allocator request size (keep well under INT_MAX): */
#define MAX_ALLOC 0x40000000
/* 定义一个虚构的哈希种子 */
/* A made-up hashing seed: */
#define HASH_CONST 0xa5b35705
/* 定义 afl-gotcpu 控制忙循环计时的常量 */
/* Constants for afl-gotcpu to control busy loop timing: */
#define CTEST_TARGET_MS 5000
#define CTEST_CORE_TRG_MS 1000
#define CTEST_BUSY_CYCLES (10 * 1000 * 1000)
/* 如果需要使用基于块覆盖的仪器,取消注释此宏 */
/* Uncomment this to use inferior block-coverage-based instrumentation. Note
that you need to recompile the target binary for this to have any effect: */
// #define COVERAGE_ONLY
/* 如果需要忽略命中计数并且每个元组只输出一个位,取消注释此宏 */
/* Uncomment this to ignore hit counts and output just one bit per tuple.
As with the previous setting, you will need to recompile the target
binary: */
// #define SKIP_COUNTS
/* 如果需要使用仪器数据记录新发现的路径,但不使用它们作为模糊测试的种子,取消注释此宏 */
/* Uncomment this to use instrumentation data to record newly discovered paths,
but do not use them as seeds for fuzzing. This is useful for conveniently
measuring coverage that could be attained by a "dumb" fuzzing algorithm: */
// #define IGNORE_FINDS
#endif /* ! _HAVE_CONFIG_H */
#endif /* ! _HAVE_CONFIG_H */

@ -155,50 +155,54 @@
* Misc terminal codes *
***********************/
// 定义一些额外的终端控制代码
#define TERM_HOME "\x1b[H" // 移动光标到终端左上角
#define TERM_CLEAR TERM_HOME "\x1b[2J" // 清除终端屏幕
#define cEOL "\x1b[0K" // 清除当前行从光标位置到行尾的内容
#define CURSOR_HIDE "\x1b[?25l" // 隐藏光标
#define CURSOR_SHOW "\x1b[?25h" // 显示光标
#define TERM_HOME "\x1b[H"
#define TERM_CLEAR TERM_HOME "\x1b[2J"
#define cEOL "\x1b[0K"
#define CURSOR_HIDE "\x1b[?25l"
#define CURSOR_SHOW "\x1b[?25h"
/************************
* Debug & error macros *
************************/
// 定义一些宏用于调试和错误处理
// 如果配置中定义了MESSAGES_TO_STDOUT则使用printf否则使用fprintf(stderr, ...)
/* Just print stuff to the appropriate stream. */
#ifdef MESSAGES_TO_STDOUT
# define SAYF(x...) printf(x)
#else
# define SAYF(x...) fprintf(stderr, x)
#endif /* ^MESSAGES_TO_STDOUT */
// 显示带前缀的警告信息
/* Show a prefixed warning. */
#define WARNF(x...) do { \
SAYF(cYEL "[!] " cBRI "WARNING: " cRST x); \
SAYF(cRST "\n"); \
} while (0)
// 显示带前缀的“正在执行”信息
/* Show a prefixed "doing something" message. */
#define ACTF(x...) do { \
SAYF(cLBL "[*] " cRST x); \
SAYF(cRST "\n"); \
} while (0)
// 显示带前缀的成功信息
/* Show a prefixed "success" message. */
#define OKF(x...) do { \
SAYF(cLGN "[+] " cRST x); \
SAYF(cRST "\n"); \
} while (0)
// 显示带前缀的严重错误信息不在afl中使用
/* Show a prefixed fatal error message (not used in afl). */
#define BADF(x...) do { \
SAYF(cLRD "\n[-] " cRST x); \
SAYF(cRST "\n"); \
} while (0)
// 以详细非操作系统致命错误消息退出程序
/* Die with a verbose non-OS fatal error message. */
#define FATAL(x...) do { \
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \
cBRI x); \
@ -207,7 +211,8 @@
exit(1); \
} while (0)
// 通过调用abort()退出程序以便提供core dump
/* Die by calling abort() to provide a core dump. */
#define ABORT(x...) do { \
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \
cBRI x); \
@ -216,7 +221,8 @@
abort(); \
} while (0)
// 以包含perror()输出的方式退出程序
/* Die while also including the output of perror(). */
#define PFATAL(x...) do { \
fflush(stdout); \
SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] SYSTEM ERROR : " \
@ -227,12 +233,16 @@
exit(1); \
} while (0)
// 根据res的值调用FAULT()或PFAULT()用于解释read()、write()等的不同失败模式
/* Die with FAULT() or PFAULT() depending on the value of res (used to
interpret different failure modes for read(), write(), etc). */
#define RPFATAL(res, x...) do { \
if (res < 0) PFATAL(x); else FATAL(x); \
} while (0)
// 定义ck_write和ck_read宏用于检查write和read操作是否成功并在失败时调用RPFATAL
/* Error-checking versions of read() and write() that call RPFATAL() as
appropriate. */
#define ck_write(fd, buf, len, fn) do { \
u32 _len = (len); \
s32 _res = write(fd, buf, _len); \
@ -245,4 +255,4 @@
if (_res != _len) RPFATAL(_res, "Short read from %s", fn); \
} while (0)
#endif /* !_HAVE_DEBUG_H */
#endif /* ! _HAVE_DEBUG_H */

@ -31,94 +31,81 @@
Other code written and maintained by Michal Zalewski <lcamtuf@google.com>
*/
// 检查_HAVE_HASH_H宏是否已定义如果没有则定义它以防止头文件被重复包含
#ifndef _HAVE_HASH_H
#define _HAVE_HASH_H
// 包含types.h头文件可能包含一些基本类型的定义
#include "types.h"
// 如果编译目标是x86_64架构则定义64位循环左移宏ROL64
#ifdef __x86_64__
#define ROL64(_x, _r) ((((u64)(_x)) << (_r)) | (((u64)(_x)) >> (64 - (_r))))
// 定义一个内联函数hash32用于计算32位哈希值x86_64架构专用
static inline u32 hash32(const void* key, u32 len, u32 seed) {
// 将key转换为u64指针
const u64* data = (u64*)key;
// 初始化哈希值h1种子与长度异或
u64 h1 = seed ^ len;
// 将长度除以8因为u64是8字节
len >>= 3;
// 循环处理每个u64数据块
while (len--) {
// 读取下一个u64数据
u64 k1 = *data++;
// 应用MurmurHash算法的步骤
k1 *= 0x87c37b91114253d5ULL;
k1 = ROL64(k1, 31);
k1 *= 0x4cf5ad432745937fULL;
// 将k1合并到h1
h1 ^= k1;
h1 = ROL64(h1, 27);
h1 = h1 * 5 + 0x52dce729;
}
// 最后的哈希值处理步骤
h1 ^= h1 >> 33;
h1 *= 0xff51afd7ed558ccdULL;
h1 ^= h1 >> 33;
h1 *= 0xc4ceb9fe1a85ec53ULL;
h1 ^= h1 >> 33;
// 返回最终的哈希值
return h1;
const u64* data = (u64*)key;
u64 h1 = seed ^ len;
len >>= 3;
while (len--) {
u64 k1 = *data++;
k1 *= 0x87c37b91114253d5ULL;
k1 = ROL64(k1, 31);
k1 *= 0x4cf5ad432745937fULL;
h1 ^= k1;
h1 = ROL64(h1, 27);
h1 = h1 * 5 + 0x52dce729;
}
h1 ^= h1 >> 33;
h1 *= 0xff51afd7ed558ccdULL;
h1 ^= h1 >> 33;
h1 *= 0xc4ceb9fe1a85ec53ULL;
h1 ^= h1 >> 33;
return h1;
}
// 如果编译目标不是x86_64架构则定义32位循环左移宏ROL32
#else
#define ROL32(_x, _r) ((((u32)(_x)) << (_r)) | (((u32)(_x)) >> (32 - (_r))))
// 定义一个内联函数hash32用于计算32位哈希值非x86_64架构专用
static inline u32 hash32(const void* key, u32 len, u32 seed) {
// 将key转换为u32指针
const u32* data = (u32*)key;
// 初始化哈希值h1种子与长度异或
u32 h1 = seed ^ len;
// 将长度除以4因为u32是4字节
len >>= 2;
// 循环处理每个u32数据块
while (len--) {
// 读取下一个u32数据
u32 k1 = *data++;
// 应用MurmurHash算法的步骤
k1 *= 0xcc9e2d51;
k1 = ROL32(k1, 15);
k1 *= 0x1b873593;
// 将k1合并到h1
h1 ^= k1;
h1 = ROL32(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
}
// 最后的哈希值处理步骤
h1 ^= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >> 16;
// 返回最终的哈希值
return h1;
const u32* data = (u32*)key;
u32 h1 = seed ^ len;
len >>= 2;
while (len--) {
u32 k1 = *data++;
k1 *= 0xcc9e2d51;
k1 = ROL32(k1, 15);
k1 *= 0x1b873593;
h1 ^= k1;
h1 = ROL32(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
}
h1 ^= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >> 16;
return h1;
}
#endif /* ^__x86_64__ */
// 结束宏定义_HAVE_HASH_H
#endif /* !_HAVE_HASH_H */

@ -26,28 +26,20 @@
#include <stdlib.h>
#include <unistd.h>
// 定义程序的主函数接收命令行参数个数argc和参数值argv
int main(int argc, char** argv) {
// 定义一个字符数组buf大小为8用于存储从标准输入读取的数据
char buf[8];
// 尝试从文件描述符0标准输入读取最多8个字节的数据到buf中
if (read(0, buf, 8) < 1) {
// 如果读取的字节数小于1即读取失败则打印消息并退出程序
printf("Hum?\n");
exit(1);
}
// 检查buf的第一个字符是否为'0'
if (buf[0] == '0')
// 如果是'0',则打印一条消息表示检测到零值
printf("Looks like a zero to me!\n");
else
// 如果不是'0',则打印一条消息表示检测到非零值
printf("A non-zero value? How quaint!\n");
// 正常退出程序
exit(0);
char buf[8];
if (read(0, buf, 8) < 1) {
printf("Hum?\n");
exit(1);
}
if (buf[0] == '0')
printf("Looks like a zero to me!\n");
else
printf("A non-zero value? How quaint!\n");
exit(0);
}

@ -28,20 +28,14 @@
// TODO(metzman): Create a test/ directory to store this and other similar
// files.
// 定义一个名为LLVMFuzzerTestOneInput的函数用于接收一个字节数组buf和其大小size
int LLVMFuzzerTestOneInput(uint8_t* buf, size_t size) {
// 检查输入数据的大小是否小于2如果是则返回0表示测试不通过或无需进一步处理
if (size < 2)
return 0;
// 检查输入数据的第一个字节是否为'0'
if (buf[0] == '0')
// 如果是'0',则打印一条消息表示检测到零值
printf("Looks like a zero to me!\n");
else
// 如果不是'0',则打印一条消息表示检测到非零值
printf("A non-zero value? How quaint!\n");
// 函数返回0表示测试通过或没有发现异常
if (size < 2)
return 0;
if (buf[0] == '0')
printf("Looks like a zero to me!\n");
else
printf("A non-zero value? How quaint!\n");
return 0;
}

@ -48,32 +48,27 @@ typedef uint32_t u32;
*/
// 条件编译指令用于定义64位无符号整数类型
#ifdef __x86_64__
typedef unsigned long long u64; // 在x86_64架构下使用unsigned long long作为u64
typedef unsigned long long u64;
#else
typedef uint64_t u64; // 否则使用标准库中的uint64_t作为u64
typedef uint64_t u64;
#endif /* ^__x86_64__ */
// 定义有符号整数类型
typedef int8_t s8; // 8位有符号整数
typedef int16_t s16; // 16位有符号整数
typedef int32_t s32; // 32位有符号整数
typedef int64_t s64; // 64位有符号整数
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
// 如果没有定义MIN则定义宏MIN和MAX
#ifndef MIN
# define MIN(_a,_b) ((_a) > (_b) ? (_b) : (_a)) // 取两个值中的最小值
# define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b)) // 取两个值中的最大值
# define MIN(_a,_b) ((_a) > (_b) ? (_b) : (_a))
# define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b))
#endif /* !MIN */
// 宏定义SWAP16用于交换16位值的字节序
#define SWAP16(_x) ({ \
u16 _ret = (_x); \
(u16)((_ret << 8) | (_ret >> 8)); \
})
// 宏定义SWAP32用于交换32位值的字节序
#define SWAP32(_x) ({ \
u32 _ret = (_x); \
(u32)((_ret << 24) | (_ret >> 24) | \
@ -81,23 +76,19 @@ typedef int64_t s64; // 64λ
((_ret >> 8) & 0x0000FF00)); \
})
// 条件编译指令,用于定义随机数宏
#ifdef AFL_LLVM_PASS
# define AFL_R(x) (random() % (x)) // 在AFL_LLVM_PASS模式下使用
# define AFL_R(x) (random() % (x))
#else
# define R(x) (random() % (x)) // 否则使用
# define R(x) (random() % (x))
#endif /* ^AFL_LLVM_PASS */
// 宏定义STRINGIFY_INTERNAL和STRINGIFY用于将宏参数转换为字符串
#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
// 内存屏障用于阻止编译器和CPU对指令重排
#define MEM_BARRIER() \
__asm__ volatile("" ::: "memory")
// 宏定义likely和unlikely用于优化分支预测
#define likely(_x) __builtin_expect(!!(_x), 1) // 预期_x为真
#define unlikely(_x) __builtin_expect(!!(_x), 0) // 预期_x为假
#define likely(_x) __builtin_expect(!!(_x), 1)
#define unlikely(_x) __builtin_expect(!!(_x), 0)
#endif /* ! _HAVE_TYPES_H */

Loading…
Cancel
Save