原创Paper|AFLFuzzQEMU新版适配:深度解析Patch细节

admin 2025-12-22 04:04:35 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 这篇文章深度解析了AFL++对QEMU的patch细节,特别是针对MIPS架构的适配。文章详细介绍了AFL++如何修改QEMU的多个核心文件以支持fuzzing,包括实现QASan(QEMUAddressSanitizer)用于检测内存错误,以及AFLforkserver工作模式、持久化模式和覆盖率跟踪等核心功能。文章提供了具体的代码修改示例,展示了AFL++如何通过插桩和系统调用拦截来实现高效的二进制fuzzing,对于理解AFL++的QEMU模式实现具有重要参考价值。 综合评分: 93 文章分类: 二进制安全,漏洞分析,安全工具,代码审计,安全开发


cover_image

原创 Paper | AFL Fuzz QEMU 新版适配:深度解析 Patch 细节

原创

404实验室

知道创宇404实验室

2025年12月18日 11:30 湖北

者:知道创宇404实验室

本文将深度解析 AFL++ 对 QEMU 的 patch 细节。

AFL 对 QEMU 的修改

参考资料

首先,下面列出 AFL对 QEMU 的修改目录:

 accel/tcg/cpu-exec.c                          | 1582 +++++++++++++++++
 accel/tcg/tcg-runtime.c                       |  824 +++++++++
 accel/tcg/tcg-runtime.h                       |   28 +
 accel/tcg/translate-all.c                     |  212 +++
 accel/tcg/translator.c                        |   30 +
 linux-user/elfload.c                          |   55 +
 linux-user/main.c                             |  120 ++
 linux-user/mips/cpu_loop.c                    |   15 +
 linux-user/mmap.c                             |   63 +
 linux-user/signal.c                           |   42 +-
 linux-user/syscall.c                          |   52 +-
 qemuafl/api.h                                 |  215 +++
 qemuafl/asan-giovese-inl.h                    | 1536 ++++++++++++++++
 qemuafl/asan-giovese.h                        |  155 ++
 qemuafl/common.h                              |  200 +++
 qemuafl/cpu-translate.h                       |  177 ++
 qemuafl/imported/afl_hash.h                   |   74 +
 qemuafl/imported/cmplog.h                     |  106 ++
 qemuafl/imported/config.h                     |  591 ++++++
 qemuafl/imported/snapshot-inl.h               |  115 ++
 qemuafl/imported/types.h                      |  253 +++
 qemuafl/interval-tree/.gitignore              |    3 +
 qemuafl/interval-tree/COPYING                 |   20 +
 qemuafl/interval-tree/compiler.h              |   17 +
 qemuafl/interval-tree/interval-tree.inl       |    2 +
 qemuafl/interval-tree/interval_tree_generic.h |  193 ++
 qemuafl/interval-tree/rbtree.h                |  108 ++
 qemuafl/interval-tree/rbtree.inl              |  549 ++++++
 qemuafl/interval-tree/rbtree_augmented.h      |  245 +++
 qemuafl/qasan-qemu.h                          |  143 ++
 qemuafl/qasan.h                               |  264 +++
 qemuafl/qemu-ijon-support.h                   |   65 +
 target/mips/tcg/translate.c                   |  146 ++
 tcg/tcg-op.c                                  |   19 +
 tcg/tcg.c                                     |   13 +

不过上面的目录并不完全,只针对了目标架构为 mips 的情况,不同架构的以下文件不会一样:

linux-user/{arch}/cpu_loop.c
target/{arch}/tcg/translate.c

首先,qemuafl目录下的文件为 AFL 相关的头文件,包含相关全局变量结构体类型声明等等。若需查看 AFL 对 QEMU 进行了哪些修改,可以通过检查该文件代码是否包含qemuafl目录下的头文件。

由于 AFL 的 QEMU 使用的是user-mode,因此应从 linux-user 目录下的代码开始分析。

1.linux-user/main.c文件的代码,patch 内容如下所示:

diff --git a/linux-user/main.c b/linux-user/main.c
index 2cd867491b..b0172d86fb 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -68,6 +68,9 @@
&nbsp;#define&nbsp;AT_FLAGS_PRESERVE_ARGV0 (1 << AT_FLAGS_PRESERVE_ARGV0_BIT)
&nbsp;#endif

+#include&nbsp;"tcg/tcg-op.h"
+#include&nbsp;"qemuafl/qasan-qemu.h"
+
&nbsp;char *exec_path;
&nbsp;char real_exec_path[PATH_MAX];

@@ -267,6 +270,73 @@ CPUArchState *cpu_copy(CPUArchState *env)
&nbsp; &nbsp; &nbsp;return new_env;
&nbsp;}

+/* A shorthand way to suppress the warnings that you are ignoring the return value of asprintf() */
+static inline void ignore_result(long long int unused_result)
+{
+ &nbsp; &nbsp;(void) unused_result;
+}
+
+/* Get libqasan path. */
+#ifndef&nbsp;AFL_PATH
+ &nbsp;#define&nbsp;AFL_PATH "/usr/local/lib/afl/"
+#endif
+static char *get_libqasan_path(char *own_loc)
+{
+ &nbsp; &nbsp;if (!unlikely(own_loc)) {
+ &nbsp; &nbsp; &nbsp; &nbsp;fprintf(stderr, "BUG: param own_loc is NULL\n");
+ &nbsp; &nbsp; &nbsp; &nbsp;exit(EXIT_FAILURE);
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;char *tmp, *cp = NULL, *rsl, *own_copy;
+
+ &nbsp; &nbsp;tmp = getenv("AFL_PATH");
+ &nbsp; &nbsp;if (tmp) {
+ &nbsp; &nbsp; &nbsp; &nbsp;ignore_result(asprintf(&cp, "%s/libqasan.so", tmp));
+ &nbsp; &nbsp; &nbsp; &nbsp;if (access(cp, X_OK)) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fprintf(stderr, "Unable to find '%s'\n", tmp);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;exit(EXIT_FAILURE);
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp; &nbsp; &nbsp;return cp;
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;own_copy = strdup(own_loc);
+ &nbsp; &nbsp;rsl = strrchr(own_copy, '/');
+ &nbsp; &nbsp;if (rsl) {
+ &nbsp; &nbsp; &nbsp; &nbsp;*rsl = 0;
+
+ &nbsp; &nbsp; &nbsp; &nbsp;ignore_result(asprintf(&cp, "%s/libqasan.so", own_copy));
+ &nbsp; &nbsp; &nbsp; &nbsp;free(own_copy);
+
+ &nbsp; &nbsp; &nbsp; &nbsp;if (!access(cp, X_OK)) { return cp; }
+
+ &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp;free(own_copy);
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;if (!access(AFL_PATH "/libqasan.so", X_OK)) {
+ &nbsp; &nbsp; &nbsp; &nbsp;if (cp) { free(cp); }
+
+ &nbsp; &nbsp; &nbsp; &nbsp;return strdup(AFL_PATH "/libqasan.so");
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;/* This is an AFL error message, but since it is in QEMU it can't
+ &nbsp; &nbsp; &nbsp; have all the pretty formatting of AFL without importing
+ &nbsp; &nbsp; &nbsp; a bunch of AFL pieces. */
+ &nbsp; &nbsp;fprintf(stderr, "\n" "" "[-] " ""
+ &nbsp; &nbsp; &nbsp; &nbsp;"Oops, unable to find the 'libqasan.so' binary. The binary must be "
+ &nbsp; &nbsp; &nbsp; &nbsp;"built\n"
+ &nbsp; &nbsp; &nbsp; &nbsp;" &nbsp; &nbsp;separately by following the instructions in "
+ &nbsp; &nbsp; &nbsp; &nbsp;"qemu_mode/libqasan/README.md. "
+ &nbsp; &nbsp; &nbsp; &nbsp;"If you\n"
+ &nbsp; &nbsp; &nbsp; &nbsp;" &nbsp; &nbsp;already have the binary installed, you may need to specify "
+ &nbsp; &nbsp; &nbsp; &nbsp;"AFL_PATH in the\n"
+ &nbsp; &nbsp; &nbsp; &nbsp;" &nbsp; &nbsp;environment.\n");
+
+ &nbsp; &nbsp;fprintf(stderr, "Failed to locate 'libqasan.so'.\n");
+ &nbsp; &nbsp;exit(EXIT_FAILURE);
+}
+
&nbsp;static void handle_arg_help(const char *arg)
&nbsp;{
&nbsp; &nbsp; &nbsp;usage(EXIT_SUCCESS);
@@ -713,6 +783,18 @@ int main(int argc, char **argv, char **envp)
&nbsp; &nbsp; &nbsp;unsigned long max_reserved_va;
&nbsp; &nbsp; &nbsp;bool preserve_argv0;

+ &nbsp; &nbsp;use_qasan = !!getenv("AFL_USE_QASAN");
+
+ &nbsp; &nbsp;if (getenv("QASAN_MAX_CALL_STACK"))
+ &nbsp; &nbsp; &nbsp;qasan_max_call_stack = atoi(getenv("QASAN_MAX_CALL_STACK"));
+ &nbsp; &nbsp;if (getenv("QASAN_SYMBOLIZE"))
+ &nbsp; &nbsp; &nbsp;qasan_symbolize = atoi(getenv("QASAN_SYMBOLIZE"));
+
+#if&nbsp;defined(ASAN_GIOVESE) && !defined(DO_NOT_USE_QASAN)
+ &nbsp; &nbsp;if (use_qasan)
+ &nbsp; &nbsp; &nbsp;asan_giovese_init();
+#endif
+
&nbsp; &nbsp; &nbsp;error_init(argv[0]);
&nbsp; &nbsp; &nbsp;module_call_init(MODULE_INIT_TRACE);
&nbsp; &nbsp; &nbsp;qemu_init_cpu_list();
@@ -733,6 +815,45 @@ int main(int argc, char **argv, char **envp)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(void) envlist_setenv(envlist, *wrk);
&nbsp; &nbsp; &nbsp;}

+ &nbsp; &nbsp;/* Add AFL_PRELOAD for qasan if it is enabled */
+ &nbsp; &nbsp;if(use_qasan) {
+ &nbsp; &nbsp; &nbsp; &nbsp;char *preload = getenv("AFL_PRELOAD");
+ &nbsp; &nbsp; &nbsp; &nbsp;char *libqasan = get_libqasan_path(argv[0]);
+
+ &nbsp; &nbsp; &nbsp; &nbsp;if (!preload) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;setenv("AFL_PRELOAD", libqasan, 0);
+ &nbsp; &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* NOTE: If there is more than one in the list, LD_PRELOAD allows spaces or colons
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; as separators (but no escaping provided), but DYLD_INSERT_LIBRARIES allows only colons.
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Prefer colons for maximum compatibility, but use space if the string already has any. */
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;char * afl_preload;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (strchr(preload, ' ')) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ignore_result(asprintf(&afl_preload, "%s %s", libqasan, preload));
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ignore_result(asprintf(&afl_preload, "%s:%s", libqasan, preload));
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;setenv("AFL_PRELOAD", afl_preload, 1);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;free(afl_preload);
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp;free(libqasan);
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;/* Expand AFL_PRELOAD to append preload libraries */
+ &nbsp; &nbsp;char *afl_preload = getenv("AFL_PRELOAD");
+ &nbsp; &nbsp;if (afl_preload) {
+ &nbsp; &nbsp; &nbsp; &nbsp;/* NOTE: If there is more than one in the list, LD_PRELOAD allows spaces or colons
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; as separators, but DYLD_INSERT_LIBRARIES allows only colons.
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Maybe we should attempt to normalize the list here before we assign it? */
+ &nbsp; &nbsp; &nbsp; &nbsp;char * ld_preload;
+ &nbsp; &nbsp; &nbsp; &nbsp;ignore_result(asprintf(&ld_preload, "LD_PRELOAD=%s", afl_preload));
+ &nbsp; &nbsp; &nbsp; &nbsp;envlist_setenv(envlist, ld_preload);
+
+ &nbsp; &nbsp; &nbsp; &nbsp;char * dyld_insert;
+ &nbsp; &nbsp; &nbsp; &nbsp;ignore_result(asprintf(&dyld_insert, "DYLD_INSERT_LIBRARIES=%s", afl_preload));
+ &nbsp; &nbsp; &nbsp; &nbsp;envlist_setenv(envlist, dyld_insert);
+ &nbsp; &nbsp;}
+
&nbsp; &nbsp; &nbsp;/* Read the stack limit from the kernel. &nbsp;If it's "unlimited",
&nbsp; &nbsp; &nbsp; &nbsp; then we can do little else besides use the default. &nbsp;*/
&nbsp; &nbsp; &nbsp;{

main.c 代码中添加的内容旨在让 QEMU 支持 QASan。关于 QASan,ChatGPT 的解释如下:

QASan(QEMU Address Sanitizer)
是 AFL++ 专为 QEMU 模式实现的一种轻量级 Address Sanitizer(内存错误检测)。
在 QEMU 的 linux-user 模式中,通过 LD_PRELOAD + runtime hook 去检测目标程序的内存错误。
QASan 可以检测:
✔️ 堆缓冲区越界(heap OOB)
例如 malloc 100 字节,但写到 100 以外的区域。
✔️ Use-after-free(UAF)
free 之后继续使用。
✔️ Double-free
同一个指针重复释放。
✔️ Invalid free
释放不是 malloc 得到的地址。
✔️ 一些栈溢出触发崩溃行为
(栈上的 shadow memory 不完整,所以能力有限,但比没有强。)
✔️ 内存泄漏检测(部分)
它覆盖的是 heap 和全局区内存错误。
栈检测有限,但也能帮助 fuzzing 提升覆盖率和能检测更多 bug。

2.linux-user/elfload.c文件patch 内容如下所示:

diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index fa83d78667..e8b1a946fb 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -33,6 +33,8 @@
&nbsp;#include&nbsp;"target/arm/cpu-features.h"
&nbsp;#endif

+#include&nbsp;"qemuafl/common.h"
+
&nbsp;#ifdef&nbsp;_ARCH_PPC64
&nbsp;#undef&nbsp;ARCH_DLINFO
&nbsp;#undef&nbsp;ELF_PLATFORM
@@ -3463,9 +3465,11 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (elf_prot & PROT_EXEC) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (vaddr < info->start_code) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;info->start_code = vaddr;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (!afl_start_code) afl_start_code = vaddr;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (vaddr_ef > info->end_code) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;info->end_code = vaddr_ef;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (!afl_end_code) afl_end_code = vaddr_ef;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (elf_prot & PROT_WRITE) {
@@ -3499,6 +3503,57 @@ static void load_elf_image(const char *image_name, const ImageSource *src,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;load_symbols(ehdr, src, load_bias);
&nbsp; &nbsp; &nbsp;}

+ &nbsp; &nbsp;if (getenv("AFL_QEMU_BLOCK_COV")) {
+ &nbsp; &nbsp; &nbsp;block_cov = 1;
+ &nbsp; &nbsp; &nbsp;block_id = 5;
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;if (!afl_exit_point) {
+ &nbsp; &nbsp; &nbsp;char *ptr;
+ &nbsp; &nbsp; &nbsp;if ((ptr = getenv("AFL_EXITPOINT")) != NULL) {
+ &nbsp; &nbsp; &nbsp; &nbsp;afl_exit_point = strtoul(ptr, NULL, 16);
+#ifdef&nbsp;TARGET_ARM
+ &nbsp; &nbsp; &nbsp;/* The least significant bit indicates Thumb mode. */
+ &nbsp; &nbsp; &nbsp; &nbsp;afl_exit_point = afl_exit_point & ~(target_ulong)1;
+#endif
+ &nbsp; &nbsp; &nbsp; &nbsp;if (getenv("AFL_DEBUG") != NULL)
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fprintf(stderr, "AFL exitpoint: 0x%lx\n",
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(unsigned long)afl_exit_point);
+ &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;if (!afl_entry_point) {
+ &nbsp; &nbsp; &nbsp;char *ptr;
+ &nbsp; &nbsp; &nbsp;if ((ptr = getenv("AFL_ENTRYPOINT")) != NULL) {
+ &nbsp; &nbsp; &nbsp; &nbsp;afl_entry_point = strtoul(ptr, NULL, 16);
+ &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp;// On PowerPC64 the entry point is the _function descriptor_
+ &nbsp; &nbsp; &nbsp; &nbsp;// of the entry function. For AFL to properly initialize,
+ &nbsp; &nbsp; &nbsp; &nbsp;// afl_entry_point needs to be set to the actual first instruction
+ &nbsp; &nbsp; &nbsp; &nbsp;// as opposed executed by the target program. This as opposed to
+ &nbsp; &nbsp; &nbsp; &nbsp;// where the function's descriptor sits in memory.
+ &nbsp; &nbsp; &nbsp; &nbsp;// copied from PPC init_thread
+#if&nbsp;defined(TARGET_PPC64) && !defined(TARGET_ABI32)
+ &nbsp; &nbsp; &nbsp; &nbsp;if (get_ppc64_abi(info) < 2) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uint64_t val;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;get_user_u64(val, info->entry);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;afl_entry_point = val + info->load_bias;
+ &nbsp; &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;afl_entry_point = info->entry;
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+#else
+ &nbsp; &nbsp; &nbsp; &nbsp;afl_entry_point = info->entry;
+#endif
+ &nbsp; &nbsp; &nbsp;}
+#ifdef&nbsp;TARGET_ARM
+ &nbsp; &nbsp; &nbsp;/* The least significant bit indicates Thumb mode. */
+ &nbsp; &nbsp; &nbsp;afl_entry_point = afl_entry_point & ~(target_ulong)1;
+#endif
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;if (getenv("AFL_DEBUG") != NULL)
+ &nbsp; &nbsp; &nbsp;fprintf(stderr, "AFL forkserver entrypoint: 0x%lx\n",
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(unsigned long)afl_entry_point);
+
&nbsp; &nbsp; &nbsp;debuginfo_report_elf(image_name, src->fd, load_bias);

&nbsp; &nbsp; &nbsp;mmap_unlock();

elfload.c 文件主要用于读取目标 ELF 文件结构信息。AFL 在该文件中进行全局变量的初始化,比如获取该 ELF 文件的代码段访问。还可以通过AFL_EXITPOINT环境变量设置程序fuzz 的结束地址。还可以使用AFL_ENTRYPOINT环境变量设置fuzz 的入口地址。如果没设置,默认为 ELF 程序的代码起始地址。

3.linux-user/mmap.c文件patch 内容如下所示:

diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index d1f36e6f16..c080a739bf 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -34,6 +34,25 @@
&nbsp;#include&nbsp;"target/arm/cpu-features.h"
&nbsp;#endif

+#include&nbsp;"qemuafl/common.h"
+#include&nbsp;"qemuafl/interval-tree/interval-tree.inl"
+
+struct mmap_tree_node {
+
+ &nbsp;struct rb_node rb;
+ &nbsp;abi_long start, end;
+ &nbsp;abi_long __subtree_last;
+
+};
+
+#define&nbsp;MMAP_TREE_START(node) ((node)->start)
+#define&nbsp;MMAP_TREE_LAST(node) ((node)->end)
+
+INTERVAL_TREE_DEFINE(struct mmap_tree_node, rb, abi_long, __subtree_last,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MMAP_TREE_START, MMAP_TREE_LAST, static, mmap_tree)
+
+static struct rb_root mmap_tree_root = RB_ROOT;
+
&nbsp;static pthread_mutex_t mmap_mutex = PTHREAD_MUTEX_INITIALIZER;
&nbsp;static __thread int mmap_lock_count;

@@ -585,6 +604,12 @@ static abi_long mmap_end(abi_ulong start, abi_ulong last,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;qemu_log_unlock(f);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp;if (afl_fork_child && persistent_memory) {
+ &nbsp; &nbsp; &nbsp; &nbsp;struct mmap_tree_node* node = calloc(sizeof(struct mmap_tree_node), 1);
+ &nbsp; &nbsp; &nbsp; &nbsp;node->start = start;
+ &nbsp; &nbsp; &nbsp; &nbsp;node->end = last;
+ &nbsp; &nbsp; &nbsp; &nbsp;mmap_tree_insert(node, &mmap_tree_root);
+ &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp;return start;
&nbsp;}

@@ -1095,6 +1120,17 @@ int target_munmap(abi_ulong start, abi_ulong len)
&nbsp; &nbsp; &nbsp;if (likely(ret == 0)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;page_set_flags(start, start + len - 1, 0);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;shm_region_rm_complete(start, start + len - 1);
+
+ &nbsp; &nbsp; &nbsp; &nbsp;if (afl_fork_child && persistent_memory) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;struct mmap_tree_node* node = mmap_tree_iter_first(&mmap_tree_root,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start, start + len - 1);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;while (node) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;struct mmap_tree_node* next = mmap_tree_iter_next(node, start,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start + len - 1);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;mmap_tree_remove(node, &mmap_tree_root);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;node = next;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp;mmap_unlock();

@@ -1189,6 +1225,21 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;page_set_flags(new_addr, new_addr + new_size - 1,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; prot | PAGE_VALID | PAGE_RESET);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;shm_region_rm_complete(new_addr, new_addr + new_size - 1);
+ &nbsp; &nbsp; &nbsp; &nbsp;if (afl_fork_child && persistent_memory) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;struct mmap_tree_node* node = mmap_tree_iter_first(&mmap_tree_root,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;old_addr, old_addr + old_size - 1);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;while (node) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;struct mmap_tree_node* next = mmap_tree_iter_next(node, old_addr,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;old_addr + old_size - 1);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;mmap_tree_remove(node, &mmap_tree_root);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;node = next;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;node = calloc(sizeof(struct mmap_tree_node), 1);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;node->start = new_addr;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;node->end = new_addr + new_size - 1;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;mmap_tree_insert(node, &mmap_tree_root);
+ &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp;mmap_unlock();
&nbsp; &nbsp; &nbsp;return new_addr;
@@ -1486,3 +1537,15 @@ abi_long target_shmdt(abi_ulong shmaddr)
&nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp;return rv;
&nbsp;}
+
+void afl_target_unmap_trackeds(void) {
+
+ &nbsp; &nbsp;struct mmap_tree_node* node = mmap_tree_iter_first(&mmap_tree_root, 0,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(abi_ulong)-1);
+ &nbsp; &nbsp;while (node) {
+ &nbsp; &nbsp; &nbsp; &nbsp;struct mmap_tree_node* next = mmap_tree_iter_next(node, 0, (abi_ulong)-1);
+ &nbsp; &nbsp; &nbsp; &nbsp;target_munmap(node->start, node->end - node->start);
+ &nbsp; &nbsp; &nbsp; &nbsp;node = next;
+ &nbsp; &nbsp;}
+
+}

mmap.c 文件涉及内存管理。在 persistent 模式下,AFL 需要自动跟踪目标应用的所有 mmap 内存区域。

4.linux-user/signal.c文件patch 内容如下所示:

diff --git a/linux-user/signal.c b/linux-user/signal.c
index 4dafc2c3a2..8c99829023 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -39,6 +39,9 @@
&nbsp;#include&nbsp;"user/signal.h"
&nbsp;#include&nbsp;"tcg/tcg.h"

+#include&nbsp;"tcg/tcg-op.h"
+#include&nbsp;"qemuafl/qasan-qemu.h"
+
&nbsp;/* target_siginfo_t must fit in gdbstub's siginfo save area. */
&nbsp;QEMU_BUILD_BUG_ON(sizeof(target_siginfo_t) > MAX_SIGINFO_LENGTH);

@@ -1293,19 +1296,55 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;print_taken_signal(sig, &unswapped);
&nbsp; &nbsp; &nbsp;}

- &nbsp; &nbsp;if (handler == TARGET_SIG_DFL) {
- &nbsp; &nbsp; &nbsp; &nbsp;/* default handler : ignore some signal. The other are job control or fatal */
+ &nbsp; &nbsp;int ignore_handling = !!getenv("AFL_QEMU_FORCE_DFL");
+
+ &nbsp; &nbsp;if (handler == TARGET_SIG_DFL || ignore_handling) {
+ &nbsp; &nbsp;/* default handler : ignore some signal. The other are job control or fatal */
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN || sig == TARGET_SIGTTOU) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kill(getpid(),SIGSTOP);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} else if (sig != TARGET_SIGCHLD &&
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sig != TARGET_SIGURG &&
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sig != TARGET_SIGWINCH &&
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sig != TARGET_SIGCONT) {
+#if&nbsp;defined(ASAN_GIOVESE) && !defined(DO_NOT_USE_QASAN)
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (use_qasan) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (sig == TARGET_SIGILL ||
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sig != TARGET_SIGFPE ||
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sig != TARGET_SIGSEGV ||
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sig != TARGET_SIGBUS)
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;asan_giovese_deadly_signal(target_to_host_signal(sig),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; k->info._sifields._sigfault._addr,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PC_GET(cpu_env), BP_GET(cpu_env),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SP_GET(cpu_env));
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;else
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;asan_giovese_deadly_signal(target_to_host_signal(sig),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PC_GET(cpu_env),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PC_GET(cpu_env), BP_GET(cpu_env),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SP_GET(cpu_env));
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+#endif
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dump_core_and_abort(cpu_env, sig);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp;} else if (handler == TARGET_SIG_IGN) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* ignore sig */
&nbsp; &nbsp; &nbsp;} else if (handler == TARGET_SIG_ERR) {
+#if&nbsp;defined(ASAN_GIOVESE) && !defined(DO_NOT_USE_QASAN)
+ &nbsp; &nbsp; &nbsp;if (use_qasan) {
+ &nbsp; &nbsp; &nbsp; &nbsp;if (sig == TARGET_SIGILL ||
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sig == TARGET_SIGFPE ||
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sig == TARGET_SIGSEGV ||
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sig == TARGET_SIGBUS)
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;asan_giovese_deadly_signal(target_to_host_signal(sig),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; k->info._sifields._sigfault._addr,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PC_GET(cpu_env), BP_GET(cpu_env),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SP_GET(cpu_env));
+ &nbsp; &nbsp; &nbsp; &nbsp;else
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;asan_giovese_deadly_signal(target_to_host_signal(sig),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PC_GET(cpu_env),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PC_GET(cpu_env), BP_GET(cpu_env),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SP_GET(cpu_env));
+ &nbsp; &nbsp; &nbsp;}
+#endif
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dump_core_and_abort(cpu_env, sig);

signal.c 文件主要处理信号相关内容。AFL 主要通过信号来判断目标程序是否 crash,比如:SIGILLSIGFPESIGSEGVSIGBUS

然而,当目标程序自行编写信号处理函数时,AFL 可能无法捕获相关信号。因此,可通过

除此之外,还有添加跟QASan相关的代码,在 fatal signals 之前调用 QASan。

5.linux-user/syscall.c文件patch 内容如下所示:

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 3a25abfaca..d6612ace5a 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -148,6 +148,9 @@
&nbsp;#include&nbsp;"fd-trans.h"
&nbsp;#include&nbsp;"user/cpu_loop.h"

+#include&nbsp;"qemuafl/common.h"
+#include&nbsp;"qemuafl/qasan-qemu.h"
+
&nbsp;#ifndef&nbsp;CLONE_IO
&nbsp;#define&nbsp;CLONE_IO &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0x80000000 &nbsp; &nbsp; &nbsp;/* Clone io context */
&nbsp;#endif
@@ -847,6 +850,15 @@ void target_set_brk(abi_ulong new_brk)
&nbsp; &nbsp; &nbsp;initial_target_brk = target_brk;
&nbsp;}

+abi_ulong afl_get_brk(void) {
+ &nbsp;return target_brk;
+}
+abi_ulong afl_set_brk(abi_ulong new_brk) {
+ &nbsp;abi_ulong old_brk = target_brk;
+ &nbsp;target_brk = new_brk;
+ &nbsp;return old_brk;
+}
+
&nbsp;/* do_brk() must return target values and target errnos. */
&nbsp;abi_long do_brk(abi_ulong brk_val)
&nbsp;{
@@ -8590,7 +8602,7 @@ static int do_execv(CPUArchState *cpu_env, int dirfd,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;abi_long guest_envp, int flags, bool is_execveat)
&nbsp;{
&nbsp; &nbsp; &nbsp;int ret;
- &nbsp; &nbsp;char **argp, **envp;
+ &nbsp; &nbsp;char **argp = NULL, **envp = NULL;
&nbsp; &nbsp; &nbsp;int argc, envc;
&nbsp; &nbsp; &nbsp;abi_ulong gp;
&nbsp; &nbsp; &nbsp;abi_ulong addr;
@@ -8616,6 +8628,35 @@ static int do_execv(CPUArchState *cpu_env, int dirfd,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (!addr) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp;/* QASAN: remove preloaded library */
+ &nbsp; &nbsp; &nbsp; &nbsp;if (use_qasan && !getenv("QASAN_PRESERVE_EXECVE")) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/*
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* If we need to clear the LD_PRELOAD list, run the memory
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* lock and unlock methods to inspect the contents within
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;* the strings.
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;abi_long len = target_strlen(gp);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (len < 0) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return -TARGET_EFAULT;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;char *env = lock_user(VERIFY_WRITE, gp, (long)(len + 1), 0);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (!env)
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;goto execve_efault;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (!strncmp("LD_PRELOAD=", env, 11)) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;char *p, *q, *r;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if ((q = r = strstr(env +11, "libqasan.so")) != NULL) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;size_t mlen = strlen("libqasan.so");
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;while ((r = strstr(p = r + mlen, "libqasan.so")) != NULL) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;while (p < r)
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*q++ = *p++;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;while ((*q++ = *p++) != '\0')
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;continue;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;unlock_user(env, gp, (long)(len + 1));
+ &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;envc++;
&nbsp; &nbsp; &nbsp;}

@@ -13864,6 +13905,15 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return ret;
&nbsp;#endif

+ &nbsp; &nbsp;case QASAN_FAKESYS_NR:
+ &nbsp; &nbsp; &nbsp; &nbsp;/* QASAN syscall */
+ &nbsp; &nbsp; &nbsp; &nbsp;if (use_qasan) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return qasan_actions_dispatcher(cpu_env, arg1, arg2, arg3, arg4);
+ &nbsp; &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fprintf(stderr, "QAsan syscall unsupported without enabling QASan mode (AFL_USE_QASAN)\n");
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return -TARGET_ENOSYS;
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+
&nbsp;#if&nbsp;defined(TARGET_NR_pivot_root)

syscall.c 文件主要处理系统调用相关指令。该补丁主要进行了三类修改:

  • 新增 brk 相关的 AFL/QASAN 辅助函数
  • execve 环境变量处理增强:自动移除 LD_PRELOAD 中的 libqasan.so
  • 新增 QASAN 的 “fake syscall” 接口,用于与 QASAN 交互

6.linux-user/mips/cpu_loop.c文件patch 内容如下所示:

diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c
index 6405806eb0..ce6a620b7f 100644
--- a/linux-user/mips/cpu_loop.c
+++ b/linux-user/mips/cpu_loop.c
@@ -26,6 +26,9 @@
&nbsp;#include&nbsp;"internal.h"
&nbsp;#include&nbsp;"fpu_helper.h"

+/* MIPS_PATCH */
+#include&nbsp;"qemuafl/common.h"
+
&nbsp;# ifdef TARGET_ABI_MIPSO32
&nbsp;# &nbsp;define MIPS_SYSCALL_NUMBER_UNUSED -1
&nbsp;static const int8_t mips_syscall_args[] = {
@@ -78,6 +81,18 @@ void cpu_loop(CPUMIPSState *env)

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;switch(trapnr) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;case EXCP_SYSCALL:
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;persistent_exits &&
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; env->active_tc.gpr[2] == TARGET_NR_exit_group ||
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // uclibc may use the following signal instead of
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // exit_group:
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; env->active_tc.gpr[2] == TARGET_NR_exit
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;)
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;env->active_tc.PC = afl_persistent_addr;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;continue;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;env->active_tc.PC += 4;
&nbsp;# ifdef TARGET_ABI_MIPSO32
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;syscall_num = env->active_tc.gpr[2] - 4000;

linux-user/{arch}/cpu_loop.c 文件包含 QEMU 执行客户端代码的主要函数。AFL 在该文件中添加的内容为:在persistent模式下,如果遇到exitexit_group函数,并不会真正的退出,而是跳转到afl_persistent_addr地址。

接下来是accel/tcg目录下的代码,在默认情况下,QEMU 仿真的流程为:

Guest CPU 指令 &nbsp;→ &nbsp;TCG IR(中间指令) → &nbsp;Host 机器码 → 执行

TCG IR 翻译为主机指令的操作即在该目录下完成。

  1. accel/tcg/tcg-runtime.h

    文件patch 内容如下所示:

diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h
index c23b5e66c4..e71cf0ca52 100644
--- a/accel/tcg/tcg-runtime.h
+++ b/accel/tcg/tcg-runtime.h
@@ -323,3 +323,31 @@ DEF_HELPER_FLAGS_4(gvec_leus32, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)
&nbsp;DEF_HELPER_FLAGS_4(gvec_leus64, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32)

&nbsp;DEF_HELPER_FLAGS_5(gvec_bitsel, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32)
+
+DEF_HELPER_FLAGS_1(afl_entry_routine, TCG_CALL_NO_RWG, void, env)
+DEF_HELPER_FLAGS_1(afl_persistent_routine, TCG_CALL_NO_RWG, void, env)
+DEF_HELPER_FLAGS_1(afl_maybe_log, TCG_CALL_NO_RWG, void, tl)
+DEF_HELPER_FLAGS_1(afl_maybe_log2, TCG_CALL_NO_RWG, void, tl)
+DEF_HELPER_FLAGS_1(afl_maybe_log_trace, TCG_CALL_NO_RWG, void, tl)
+DEF_HELPER_FLAGS_3(afl_compcov_16, TCG_CALL_NO_RWG, void, tl, tl, tl)
+DEF_HELPER_FLAGS_3(afl_compcov_32, TCG_CALL_NO_RWG, void, tl, tl, tl)
+DEF_HELPER_FLAGS_3(afl_compcov_64, TCG_CALL_NO_RWG, void, tl, tl, tl)
+DEF_HELPER_FLAGS_3(afl_cmplog_8, TCG_CALL_NO_RWG, void, tl, tl, tl)
+DEF_HELPER_FLAGS_3(afl_cmplog_16, TCG_CALL_NO_RWG, void, tl, tl, tl)
+DEF_HELPER_FLAGS_3(afl_cmplog_32, TCG_CALL_NO_RWG, void, tl, tl, tl)
+DEF_HELPER_FLAGS_3(afl_cmplog_64, TCG_CALL_NO_RWG, void, tl, tl, tl)
+DEF_HELPER_FLAGS_1(afl_cmplog_rtn, TCG_CALL_NO_RWG, void, env)
+
+DEF_HELPER_FLAGS_5(qasan_fake_instr, TCG_CALL_NO_RWG, tl, env, tl, tl, tl, tl)
+DEF_HELPER_FLAGS_2(qasan_load1, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_FLAGS_2(qasan_load2, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_FLAGS_2(qasan_load4, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_FLAGS_2(qasan_load8, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_FLAGS_2(qasan_store1, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_FLAGS_2(qasan_store2, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_FLAGS_2(qasan_store4, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_FLAGS_2(qasan_store8, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_FLAGS_1(qasan_shadow_stack_push, TCG_CALL_NO_RWG, void, tl)
+DEF_HELPER_FLAGS_1(qasan_shadow_stack_pop, TCG_CALL_NO_RWG, void, tl)
+
+DEF_HELPER_FLAGS_4(ijon_func_call, TCG_CALL_NO_RWG, void, tl, tl, tl, tl)

accel/tcg/tcg-runtime.h头文件主要是用来声明 TCG Helper函数,AFL 在该文件中新增了一些 AFL 相关的Helper 函数。

下面介绍 TCG Helper 函数的结构。

1). 在include/exec/helper-proto.h.inc函数中定义了Helper 函数声明的宏。

DEF_HELPER_FLAGS_x为Helper 函数宏,其中 x 标识有几个参数,比如:DEF_HELPER_FLAGS_2表示该函数有两个参数。

helper-proto.h.inc文件中的宏为:

#define&nbsp;DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \
dh_ctype(ret)HELPER(name)(dh_ctype(t1),dh_ctype(t2))&nbsp;DEF_HELPER_ATTR;

若将 DEF_HELPER_FLAGS_2(qasan_load2, TCG_CALL_NO_RWG, void, env, tl) 展开,函数声明为:void helper_qasan_load2(CPUArchState *, i32) __attribute__((noinline));

2). 在include/exec/helper-info.h.inc函数中定义了 Helper 函数相关的结构体宏。

helper-info.h.inc文件中的宏为:

#define&nbsp;DEF_HELPER_FLAGS_2(NAME, FLAGS, RET, T1, T2) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
&nbsp; &nbsp; TCGHelperInfo&nbsp;glue(helper_info_,&nbsp;NAME)={&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
.func&nbsp;=HELPER(NAME),.name&nbsp;=str(NAME),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
.flags&nbsp;=&nbsp;FLAGS&nbsp;|dh_callflag(RET),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
.typemask&nbsp;=dh_typemask(RET,0)|dh_typemask(T1,1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
|dh_typemask(T2,2)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
};

把该宏展开,如下所示:

TCGHelperInfo helper_info_qasan_load2&nbsp;={
.func&nbsp;=&nbsp;helper_qasan_load2,.name&nbsp;=&nbsp;qasan_load2,
.flags&nbsp;=&nbsp;TCG_CALL_NO_RWG&nbsp;|&nbsp;dh_callflag_void,
.typemask&nbsp;=......
}

3). 在include/exec/helper-gen.h.inc函数中定义了 Helper 函数调用函数宏。

helper-gen.h.inc文件中的宏为:

#define&nbsp;DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
extern&nbsp;TCGHelperInfo&nbsp;glue(helper_info_,&nbsp;name);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
staticinlinevoidglue(gen_helper_,&nbsp;name)(dh_retvar_decl(ret)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
dh_arg_decl(t1,1),dh_arg_decl(t2,2))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
tcg_gen_call2(glue(helper_info_,name).func,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
&glue(helper_info_,name),dh_retvar(ret),&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
dh_arg(t1,1),dh_arg(t2,2));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
}

把该宏展开,如下所示:

extern&nbsp;TCGHelperInfo helper_info_qasan_load2;
staticinlinevoidgen_helper_qasan_load2(TCGv_ptr arg1,&nbsp;TCGv_i32 arg2)
{
tcg_gen_call2(helper_info_qasan_load2.func,
&helper_info_qasan_load2,&nbsp;NULL,
tcgv_ptr_temp(arg1),tcgv_i32_temp(arg2));
}

最后就是HELPER 函数的实现,如下所示:

voidHELPER(qasan_load2)(CPUArchState&nbsp;*env,&nbsp;target_ulong addr){
......
}

宏展开后变为:
voidhelper_qasan_load2(CPUArchState&nbsp;*env,&nbsp;target_ulong addr){
......
}

当需要调用 helper 函数时,调用的不是helper_qasan_load2(x, x),而是gen_helper_qasan_load2(x, x)

2.accel/tcg/tcg-runtime.c文件patch 内容如下所示:

diff --git a/accel/tcg/tcg-runtime.c b/accel/tcg/tcg-runtime.c
index fa7ed9739c..56ebdb1261 100644
--- a/accel/tcg/tcg-runtime.c
+++ b/accel/tcg/tcg-runtime.c
@@ -31,6 +31,292 @@
&nbsp;#include&nbsp;"exec/helper-info.c.inc"
&nbsp;#undef&nbsp; HELPER_H

+#include&nbsp;"qemuafl/common.h"
+#include&nbsp;"qemuafl/qemu-ijon-support.h"
+
+uint32_t afl_hash_ip(uint64_t);
+
+void HELPER(ijon_func_call)(target_ulong var_addr, target_ulong var_len, target_ulong itype, target_ulong idx)
+{
+ &nbsp;uint64_t buf = 0;
+ &nbsp;memcpy(&buf, var_addr, var_len);
+ &nbsp;ijon_dispatch(itype, idx, buf);
+ &nbsp;fprintf(stderr, "trigger ijon: addr=0x%016" PRIx64 " tag=%s value %ld\n", var_addr, ijon_to_str(itype), buf);
+}
+
+void HELPER(afl_entry_routine)(CPUArchState *env) {
+
+ &nbsp;afl_forkserver(env_cpu(env));
+
+}
+
...
+
+#include&nbsp;<sys/mman.h>
+#include&nbsp;"linux-user/qemu.h" /* access_ok decls. */
+
+/*
+static int area_is_mapped(void *ptr, size_t len) {
+
+ &nbsp;char *p = ptr;
+ &nbsp;char *page = (char *)((uintptr_t)p & ~(sysconf(_SC_PAGE_SIZE) - 1));
+
+ &nbsp;int r = msync(page, (p - page) + len, MS_ASYNC);
+ &nbsp;if (r < 0) return errno != ENOMEM;
+ &nbsp;return 1;
+
+}
+*/
+
...
+/////////////////////////////////////////////////
+// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; QASAN
+/////////////////////////////////////////////////
+
+#include&nbsp;"qemuafl/qasan-qemu.h"
+
+// options
+int qasan_max_call_stack = 16; // QASAN_MAX_CALL_STACK
+int qasan_symbolize = 1; // QASAN_SYMBOLIZE
+int use_qasan = 0;
+
+__thread int qasan_disabled;
+
+__thread struct shadow_stack qasan_shadow_stack;
+
+#ifdef&nbsp;ASAN_GIOVESE
+
+#ifndef&nbsp;DO_NOT_USE_QASAN
+
+#include&nbsp;"qemuafl/asan-giovese-inl.h"
+
+#include&nbsp;<sys/types.h>
+#include&nbsp;<sys/syscall.h>
+
...
+
&nbsp;int32_t HELPER(rem_i32)(int32_t arg1, int32_t arg2)

tcg-runtime.c 原本用于实现 TCG Helper 函数。AFL 在该文件中增加了相关 Helper 函数的实现代码。

3.accel/tcg/translator.c文件patch 内容如下所示:

diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c
index ef1538b4fc..f33048f522 100644
--- a/accel/tcg/translator.c
+++ b/accel/tcg/translator.c
@@ -21,6 +21,8 @@
&nbsp;#include&nbsp;"disas/disas.h"
&nbsp;#include&nbsp;"tb-internal.h"

+#include&nbsp;"qemuafl/common.h"
+
&nbsp;static void set_can_do_io(DisasContextBase *db, bool val)
&nbsp;{
&nbsp; &nbsp; &nbsp;QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1);
@@ -167,6 +169,34 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;plugin_gen_insn_start(cpu, db);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}

+ &nbsp; &nbsp; &nbsp; &nbsp;if (db->pc_next == afl_entry_point) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;static bool first = true;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/*
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * We guard this section since we flush the translation cache after
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * we load the configuration, which in turn means we will need to
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * re-translate our block. If we were to perform this flush every
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * time (rather than just when our configuration is first loaded),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * we would just end up translation this block repeatedly.
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (first) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;afl_setup();
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/*
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * We flush the translation cache here since we may already have
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * translated some blocks and included instrumentation in them
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * before we have processed the configuration from the
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * environment variables which configures which ranges to
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * include and exclude. Therefore we may have some blocks in our
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * cache which are incorrectly instrumented and cause some
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * fuzzing stability or performance problems.
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tb_flush(cpu);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;first = false;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;gen_helper_afl_entry_routine(cpu_env);
+ &nbsp; &nbsp; &nbsp; &nbsp;} else if (db->pc_next == afl_exit_point) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;_exit(0);
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/*
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * Disassemble one instruction. &nbsp;The translate_insn hook should
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; * update db->pc_next and db->is_jmp to indicate what should be

QEMU 进行 Guest CPU 指令 → TCG IR(中间指令) 翻译的主要流程代码位于 translator.c 的 translator_loop 函数中。

AFL 在该函数中添加了入口流程的代码,当翻译的地址为afl_entry_point时,则调用gen_helper_afl_entry_routine函数,而该函数就是 AFL forkserver 模式通信的核心函数。

AFL forkserver 工作模式如下:

AFL 首先 fork 出一个子进程,随后创建两个管道,一个负责输入,一个负责输出。默认情况下,这两个管道的描述符为:FORKSRV_FDFORKSRV_FD+1

qemuafl/imported/config.h中定义了:#define&nbsp;FORKSRV_FD 198

其中FORKSRV_FD负责 AFL->QEMU 通信,FORKSRV_FD+1负责QEMU->AFL 通信。

创建该管道的子进程使用 execve 执行 QEMU,QEMU 进程将继承这两个管道,从而实现 AFL 和 QEMU 的进程间通信。

gen_helper_afl_entry_routine函数的实现代码为:

voidHELPER(afl_entry_routine)(CPUArchState&nbsp;*env){
afl_forkserver(env_cpu(env));
}

实际上调用的是 afl_forkserver 函数,该函数用于与 AFL 进行握手。若握手失败,则退出并进入原本的 QEMU 流程;若握手成功,则该进程保持与 AFL 通信,并 fork 出一个子进程以继续 QEMU 的后续流程。

由于是 fork 出的子进程,将完全拷贝一份内存数据,且不影响父进程的内存空间结构。AFL 可借此快速执行每次 fuzz 流程。

在 afl_forkserver 函数中,还会通过 afl_wait_tsl 函数接收子进程指令翻译的情况,并同步至父进程。这样父进程下次 fork 的子进程可直接执行 Host 机器码,无需重复指令翻译流程,从而大幅提升 QEMU 仿真速度。

4.accel/tcg/translate-all.c文件patch 内容如下所示:

diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index a497c54b80..7a6554b730 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -67,6 +67,104 @@
&nbsp;#include&nbsp;"tcg/perf.h"
&nbsp;#include&nbsp;"tcg/insn-start-words.h"

+#include&nbsp;"qemuafl/common.h"
+#include&nbsp;"tcg/tcg-op.h"
+#include&nbsp;"qemuafl/imported/afl_hash.h"
+
+#include&nbsp;<math.h>
+
+__thread int cur_block_is_good;
+
+static int afl_track_unstable_log_fd(void) {
+ &nbsp; &nbsp;static bool initialized = false;
+ &nbsp; &nbsp;static int track_fd = -1;
+ &nbsp; &nbsp;if (unlikely(!initialized)) {
+ &nbsp; &nbsp; &nbsp; &nbsp;char * fname = getenv("AFL_QEMU_TRACK_UNSTABLE");
+ &nbsp; &nbsp; &nbsp; &nbsp;if (fname != NULL) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;track_fd = open(fname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR);
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp;initialized = true;
+ &nbsp; &nbsp; &nbsp; &nbsp;if (track_fd > 0) dprintf(track_fd, "QEMU UNSTABLE TRACKING ENABLED\n");
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;return track_fd;
+}
+
+void HELPER(afl_maybe_log)(target_ulong cur_loc) {
+ &nbsp;register uintptr_t afl_idx = cur_loc ^ afl_prev_loc;
+
+ &nbsp;INC_AFL_AREA(afl_idx);
+
+ &nbsp;// afl_prev_loc = ((cur_loc & (MAP_SIZE - 1) >> 1)) |
+ &nbsp;// &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;((cur_loc & 1) << ((int)ceil(log2(MAP_SIZE)) -1));
+ &nbsp;afl_prev_loc = cur_loc >> 1;
+}
+
+void HELPER(afl_maybe_log2)(target_ulong cur_loc) {
+ &nbsp;register uintptr_t afl_idx = cur_loc;
+ &nbsp;INC_AFL_AREA(afl_idx);
+}
+
+void HELPER(afl_maybe_log_trace)(target_ulong cur_loc) {
+ &nbsp;register uintptr_t afl_idx = cur_loc;
+ &nbsp;INC_AFL_AREA(afl_idx);
+}
+
+static target_ulong pc_hash(target_ulong x) {
+ &nbsp; &nbsp;x = ((x >> 16) ^ x) * 0x45d9f3b;
+ &nbsp; &nbsp;x = ((x >> 16) ^ x) * 0x45d9f3b;
+ &nbsp; &nbsp;x = (x >> 16) ^ x;
+ &nbsp; &nbsp;return x;
+}
+
+/* Generates TCG code for AFL's tracing instrumentation. */
+static void afl_gen_trace(target_ulong cur_loc) {
+
+ &nbsp;/* Optimize for cur_loc > afl_end_code, which is the most likely case on
+ &nbsp; &nbsp; Linux systems. */
+
+ &nbsp;cur_block_is_good = afl_must_instrument(cur_loc);
+
+ &nbsp;if (!cur_block_is_good)
+ &nbsp; &nbsp;return;
+
+ &nbsp;/* Looks like QEMU always maps to fixed locations, so ASLR is not a
+ &nbsp; &nbsp; concern. Phew. But instruction addresses may be aligned. Let's mangle
+ &nbsp; &nbsp; the value to get something quasi-uniform. */
+
+ &nbsp;if (block_cov) {
+
+ &nbsp; &nbsp;cur_loc = block_id;
+ &nbsp; &nbsp;++block_id;
+ &nbsp; &nbsp;if (block_id >= MAP_SIZE) block_id = 5;
+
+ &nbsp; &nbsp;TCGv cur_loc_v = tcg_const_tl(cur_loc);
+ &nbsp; &nbsp;gen_helper_afl_maybe_log2(cur_loc_v);
+ &nbsp; &nbsp;tcg_temp_free(cur_loc_v);
+
+ &nbsp;} else {
+
+ &nbsp; &nbsp;// cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
+ &nbsp; &nbsp;// cur_loc &= MAP_SIZE - 1;
+ &nbsp; &nbsp;cur_loc = (uintptr_t)(afl_hash_ip((uint64_t)cur_loc));
+ &nbsp; &nbsp;cur_loc &= (MAP_SIZE - 1);
+
+ &nbsp; &nbsp;/* Implement probabilistic instrumentation by looking at scrambled block
+ &nbsp; &nbsp; &nbsp; address. This keeps the instrumented locations stable across runs. */
+
+ &nbsp; &nbsp;if (cur_loc >= afl_inst_rms) return;
+
+ &nbsp; &nbsp;TCGv cur_loc_v = tcg_const_tl(cur_loc);
+ &nbsp; &nbsp;if (unlikely(afl_track_unstable_log_fd() >= 0)) {
+ &nbsp; &nbsp; &nbsp;gen_helper_afl_maybe_log_trace(cur_loc_v);
+ &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp;gen_helper_afl_maybe_log(cur_loc_v);
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;tcg_temp_free(cur_loc_v);
+
+ &nbsp;}
+
+}
+
&nbsp;TBContext tb_ctx;

&nbsp;/*
@@ -276,6 +374,7 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
&nbsp; &nbsp; &nbsp;tcg_func_start(tcg_ctx);

&nbsp; &nbsp; &nbsp;CPUState *cs = env_cpu(env);
+ &nbsp; &nbsp;afl_gen_trace(pc);
&nbsp; &nbsp; &nbsp;tcg_ctx->cpu = cs;
&nbsp; &nbsp; &nbsp;cs->cc->tcg_ops->translate_code(cs, tb, max_insns, pc, host_pc);

@@ -283,9 +382,122 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb,
&nbsp; &nbsp; &nbsp;tcg_ctx->cpu = NULL;
&nbsp; &nbsp; &nbsp;*max_insns = tb->icount;

+ &nbsp; &nbsp;/* If we are tracking block instability, then since afl-fuzz will log the ids
+ &nbsp; &nbsp; &nbsp; of the unstable blocks, in fuzzer_stats, we must log these alongside the
+ &nbsp; &nbsp; &nbsp; instruction pointer so that the user can associate these back with the
+ &nbsp; &nbsp; &nbsp; actual binary */
+ &nbsp; &nbsp;int track_fd = afl_track_unstable_log_fd();
+ &nbsp; &nbsp;if (unlikely(track_fd >= 0)) {
+ &nbsp; &nbsp; &nbsp;uint64_t &nbsp;ip = (uint64_t)pc;
+ &nbsp; &nbsp; &nbsp;uintptr_t block_id = (uintptr_t)(afl_hash_ip(ip));
+ &nbsp; &nbsp; &nbsp;block_id &= (MAP_SIZE - 1);
+ &nbsp; &nbsp; &nbsp;dprintf(track_fd, "BLOCK ID: 0x%016" PRIx64 ", PC: 0x%016zx-0x%016zx\n",
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;block_id, ip, ip + tb->size);
+ &nbsp; &nbsp;}
+
&nbsp; &nbsp; &nbsp;return tcg_gen_code(tcg_ctx, tb, pc);
&nbsp;}

+/* Called with mmap_lock held for user mode emulation. &nbsp;*/
+TranslationBlock *afl_gen_edge(CPUState *cpu, unsigned long afl_id)
+{
+ &nbsp; &nbsp;CPUArchState *env = cpu->env_ptr;
+ &nbsp; &nbsp;TranslationBlock *tb;
+ &nbsp; &nbsp;tcg_insn_unit *gen_code_buf;
+ &nbsp; &nbsp;int gen_code_size, search_size;
+
+ &nbsp; &nbsp;assert_memory_lock();
+
+ buffer_overflow1:
+ &nbsp; &nbsp;tb = tcg_tb_alloc(tcg_ctx);
+ &nbsp; &nbsp;if (unlikely(!tb)) {
+ &nbsp; &nbsp; &nbsp; &nbsp;/* flush must be done */
+ &nbsp; &nbsp; &nbsp; &nbsp;tb_flush(cpu);
+ &nbsp; &nbsp; &nbsp; &nbsp;mmap_unlock();
+ &nbsp; &nbsp; &nbsp; &nbsp;/* Make the execution loop process the flush as soon as possible. &nbsp;*/
+ &nbsp; &nbsp; &nbsp; &nbsp;cpu->exception_index = EXCP_INTERRUPT;
+ &nbsp; &nbsp; &nbsp; &nbsp;cpu_loop_exit(cpu);
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;gen_code_buf = tcg_ctx->code_gen_ptr;
+ &nbsp; &nbsp;tb->tc.ptr = gen_code_buf;
+ &nbsp; &nbsp;tb->pc = 0;
+ &nbsp; &nbsp;tb->cs_base = 0;
+ &nbsp; &nbsp;tb->flags = 0;
+ &nbsp; &nbsp;tb->cflags = 0;
+ &nbsp; &nbsp;tb->trace_vcpu_dstate = *cpu->trace_dstate;
+ &nbsp; &nbsp;tcg_ctx->tb_cflags = 0;
+
+ &nbsp; &nbsp;tcg_func_start(tcg_ctx);
+
+ &nbsp; &nbsp;tcg_ctx->cpu = env_cpu(env);
+
+ &nbsp; &nbsp;target_ulong afl_loc = afl_id & (MAP_SIZE -1);
+ &nbsp; &nbsp;//*afl_dynamic_size = MAX(*afl_dynamic_size, afl_loc);
+ &nbsp; &nbsp;TCGv tmp0 = tcg_const_tl(afl_loc);
+ &nbsp; &nbsp;if (block_cov)
+ &nbsp; &nbsp; &nbsp;gen_helper_afl_maybe_log2(tmp0);
+ &nbsp; &nbsp;else
+ &nbsp; &nbsp; &nbsp;gen_helper_afl_maybe_log(tmp0);
+ &nbsp; &nbsp;tcg_temp_free(tmp0);
+ &nbsp; &nbsp;tcg_gen_goto_tb(0);
+ &nbsp; &nbsp;tcg_gen_exit_tb(tb, 0);
+
+ &nbsp; &nbsp;tcg_ctx->cpu = NULL;
+
+ &nbsp; &nbsp;trace_translate_block(tb, tb->pc, tb->tc.ptr);
+
+ &nbsp; &nbsp;/* generate machine code */
+ &nbsp; &nbsp;tb->jmp_reset_offset[0] = TB_JMP_RESET_OFFSET_INVALID;
+ &nbsp; &nbsp;tb->jmp_reset_offset[1] = TB_JMP_RESET_OFFSET_INVALID;
+ &nbsp; &nbsp;tcg_ctx->tb_jmp_reset_offset = tb->jmp_reset_offset;
+ &nbsp; &nbsp;if (TCG_TARGET_HAS_direct_jump) {
+ &nbsp; &nbsp; &nbsp; &nbsp;tcg_ctx->tb_jmp_insn_offset = tb->jmp_target_arg;
+ &nbsp; &nbsp; &nbsp; &nbsp;tcg_ctx->tb_jmp_target_addr = NULL;
+ &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp;tcg_ctx->tb_jmp_insn_offset = NULL;
+ &nbsp; &nbsp; &nbsp; &nbsp;tcg_ctx->tb_jmp_target_addr = tb->jmp_target_arg;
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;/* ??? Overflow could be handled better here. &nbsp;In particular, we
+ &nbsp; &nbsp; &nbsp; don't need to re-do gen_intermediate_code, nor should we re-do
+ &nbsp; &nbsp; &nbsp; the tcg optimization currently hidden inside tcg_gen_code. &nbsp;All
+ &nbsp; &nbsp; &nbsp; that should be required is to flush the TBs, allocate a new TB,
+ &nbsp; &nbsp; &nbsp; re-initialize it per above, and re-do the actual code generation. &nbsp;*/
+ &nbsp; &nbsp;gen_code_size = tcg_gen_code(tcg_ctx, tb);
+ &nbsp; &nbsp;if (unlikely(gen_code_size < 0)) {
+ &nbsp; &nbsp; &nbsp; &nbsp;goto buffer_overflow1;
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;search_size = encode_search(tb, (void *)gen_code_buf + gen_code_size);
+ &nbsp; &nbsp;if (unlikely(search_size < 0)) {
+ &nbsp; &nbsp; &nbsp; &nbsp;goto buffer_overflow1;
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;tb->tc.size = gen_code_size;
+
+ &nbsp; &nbsp;qatomic_set(&tcg_ctx->code_gen_ptr, (void *)
+ &nbsp; &nbsp; &nbsp; &nbsp;ROUND_UP((uintptr_t)gen_code_buf + gen_code_size + search_size,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CODE_GEN_ALIGN));
+
+ &nbsp; &nbsp;/* init jump list */
+ &nbsp; &nbsp;qemu_spin_init(&tb->jmp_lock);
+ &nbsp; &nbsp;tb->jmp_list_head = (uintptr_t)NULL;
+ &nbsp; &nbsp;tb->jmp_list_next[0] = (uintptr_t)NULL;
+ &nbsp; &nbsp;tb->jmp_list_next[1] = (uintptr_t)NULL;
+ &nbsp; &nbsp;tb->jmp_dest[0] = (uintptr_t)NULL;
+ &nbsp; &nbsp;tb->jmp_dest[1] = (uintptr_t)NULL;
+
+ &nbsp; &nbsp;/* init original jump addresses which have been set during tcg_gen_code() */
+ &nbsp; &nbsp;if (tb->jmp_reset_offset[0] != TB_JMP_RESET_OFFSET_INVALID) {
+ &nbsp; &nbsp; &nbsp; &nbsp;tb_reset_jump(tb, 0);
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;if (tb->jmp_reset_offset[1] != TB_JMP_RESET_OFFSET_INVALID) {
+ &nbsp; &nbsp; &nbsp; &nbsp;tb_reset_jump(tb, 1);
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;return tb;
+}
+
+
&nbsp;/* Called with mmap_lock held for user mode emulation. &nbsp;*/
&nbsp;TranslationBlock *tb_gen_code(CPUState *cpu,

translate-all.c 文件主要用于将 TCG IR 翻译成 Host 机器码

该 patch 将 AFL 的 qemu-mode 插桩深度整合进 TCG 代码,使 QEMU 在翻译TB(TCG Block,存储着 TCG IR)时自动生成各种覆盖记录,并添加了 edge TB、生存期概率插桩、不稳定 block 追踪等 AFL 功能。

5.accel/tcg/cpu-exec.c文件patch 内容如下所示:

diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index ef3d967e3a..50435b8135 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -45,6 +45,1581 @@
&nbsp;#include&nbsp;"internal-common.h"
&nbsp;#include&nbsp;"internal-target.h"

+#include&nbsp;"qemuafl/common.h"
+#include&nbsp;"qemuafl/imported/snapshot-inl.h"
+#include&nbsp;"qemuafl/qemu-ijon-support.h"
+
+#include&nbsp;<string.h>
+#include&nbsp;<sys/shm.h>
+#ifndef&nbsp;AFL_QEMU_STATIC_BUILD
+ &nbsp;#include&nbsp;<dlfcn.h>
+#endif
+
+/***************************
+ * VARIOUS AUXILIARY STUFF *
+ ***************************/
+
+/* This is equivalent to afl-as.h: */
+
+static unsigned char
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dummy[MAP_SIZE]; /* costs MAP_SIZE but saves a few instructions */
+unsigned char *afl_area_ptr = dummy; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* Exported for afl_gen_trace */
+
+/* Exported variables populated by the code patched into elfload.c: */
+
+abi_ulong afl_entry_point, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* ELF entry point (_start) */
+ &nbsp; &nbsp;afl_exit_point, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* ELF exit point &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */
+ &nbsp; &nbsp;afl_start_code, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* .text start pointer &nbsp; &nbsp; &nbsp;*/
+ &nbsp; &nbsp;afl_end_code; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* .text end pointer &nbsp; &nbsp; &nbsp; &nbsp;*/
+
+struct vmrange* afl_instr_code;
+
+abi_ulong &nbsp; &nbsp;afl_persistent_addr, afl_persistent_ret_addr;
+unsigned int afl_persistent_cnt;
+
+unsigned int block_id = 5;
+
+u8 afl_compcov_level, block_cov;
+
+__thread abi_ulong afl_prev_loc;
+
+struct cmp_map *__afl_cmp_map;
+
+/* Set in the child process in forkserver mode: */
+
+static int forkserver_installed = 0;
+static int disable_caching = 0;
+
+unsigned char afl_fork_child;
+unsigned int &nbsp;afl_forksrv_pid;
+unsigned char is_persistent;
+target_long &nbsp; persistent_stack_offset;
+unsigned char persistent_first_pass = 1;
+unsigned char persistent_exits;
+unsigned char persistent_save_gpr;
+unsigned char persistent_memory;
+int &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; persisent_retaddr_offset;
+
+struct api_regs saved_regs;
+
+u8 * shared_buf;
+u32 *shared_buf_len;
+u8 &nbsp; sharedmem_fuzzing;
+
+afl_persistent_hook_fn afl_persistent_hook_ptr;
+
+/* Instrumentation ratio: */
+
+unsigned int afl_inst_rms = MAP_SIZE; &nbsp; &nbsp; &nbsp; &nbsp; /* Exported for afl_gen_trace */
+
+/* Function declarations. */
+
+static void afl_wait_tsl(CPUState *, int);
+static void afl_request_tsl(target_ulong, target_ulong, uint32_t, uint32_t,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TranslationBlock *, int);
+
+/* Data structures passed around by the translate handlers: */
+
+struct afl_tb {
+
+ &nbsp;target_ulong pc;
+ &nbsp;target_ulong cs_base;
+ &nbsp;uint32_t &nbsp; &nbsp; flags;
+ &nbsp;uint32_t &nbsp; &nbsp; cf_mask;
+
+};
+
+struct afl_chain {
+
+ &nbsp;struct afl_tb last_tb;
+ &nbsp;uint32_t &nbsp; &nbsp; &nbsp;cf_mask;
+ &nbsp;int &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tb_exit;
+
+};
+
+struct afl_tsl {
+
+ &nbsp;struct afl_tb tb;
+ &nbsp;struct afl_chain chain;
+ &nbsp;char is_chain;
+
+};
+
+/* Some forward decls: */
+
+static inline TranslationBlock *tb_find(CPUState *, TranslationBlock *, int,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uint32_t);
+static inline void &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tb_add_jump(TranslationBlock *tb, int n,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TranslationBlock *tb_next);
+static void &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; afl_map_shm_fuzz(void);
+
+/*************************
+ * ACTUAL IMPLEMENTATION *
+ *************************/
+
+/* Snapshot memory */
+
+struct saved_region {
+
+ &nbsp;void* addr;
+ &nbsp;size_t size;
+ &nbsp;void* saved;
+
+};
+
+abi_ulong saved_brk;
+int lkm_snapshot;
+struct saved_region* memory_snapshot;
+size_t memory_snapshot_len;
+
...
+void afl_setup(void) {
+
+ &nbsp;char *id_str = getenv(SHM_ENV_VAR), *inst_r = getenv("AFL_INST_RATIO");
+
+ &nbsp;int shm_id;
+
+ &nbsp;if (inst_r) {
+
+ &nbsp; &nbsp;unsigned int r;
+
+ &nbsp; &nbsp;r = atoi(inst_r);
+
+ &nbsp; &nbsp;if (r > 100) r = 100;
+ &nbsp; &nbsp;if (!r) r = 1;
+
+ &nbsp; &nbsp;afl_inst_rms = MAP_SIZE * r / 100;
+
+ &nbsp;}
+
+ &nbsp;if (id_str) {
+
+ &nbsp; &nbsp;shm_id = atoi(id_str);
+ &nbsp; &nbsp;afl_area_ptr = shmat(shm_id, NULL, 0);
+
+ &nbsp; &nbsp;if (afl_area_ptr == (void *)-1) exit(1);
+
+ &nbsp; &nbsp;/* With AFL_INST_RATIO set to a low value, we want to touch the bitmap
+ &nbsp; &nbsp; &nbsp; so that the parent doesn't give up on us. */
+
+ &nbsp; &nbsp;if (inst_r) afl_area_ptr[0] = 1;
+
+ &nbsp;}
+
+ &nbsp;disable_caching = getenv("AFL_QEMU_DISABLE_CACHE") != NULL;
+
+ &nbsp;if (getenv("___AFL_EINS_ZWEI_POLIZEI___")) { &nbsp;// CmpLog forkserver
+
+ &nbsp; &nbsp;id_str = getenv(CMPLOG_SHM_ENV_VAR);
+
+ &nbsp; &nbsp;if (id_str) {
+
+ &nbsp; &nbsp; &nbsp;u32 shm_id = atoi(id_str);
+
+ &nbsp; &nbsp; &nbsp;__afl_cmp_map = shmat(shm_id, NULL, 0);
+
+ &nbsp; &nbsp; &nbsp;if (__afl_cmp_map == (void *)-1) exit(1);
+
+ &nbsp; &nbsp;}
+
+ &nbsp;}
+
+ &nbsp;if (getenv("AFL_INST_LIBS")) {
+
+ &nbsp; &nbsp;afl_start_code = 0;
+ &nbsp; &nbsp;afl_end_code = (abi_ulong)-1;
+
+ &nbsp;}
+
+ &nbsp;if (getenv("AFL_CODE_START"))
+ &nbsp; &nbsp;afl_start_code = strtoll(getenv("AFL_CODE_START"), NULL, 16);
+ &nbsp;if (getenv("AFL_CODE_END"))
+ &nbsp; &nbsp;afl_end_code = strtoll(getenv("AFL_CODE_END"), NULL, 16);
+
+ &nbsp;int have_names = 0;
+ &nbsp;if (getenv("AFL_QEMU_INST_RANGES")) {
+ &nbsp; &nbsp;char *str = getenv("AFL_QEMU_INST_RANGES");
+ &nbsp; &nbsp;char *saveptr1, *saveptr2 = NULL, *save_pt1 = NULL;
+ &nbsp; &nbsp;char *pt1, *pt2, *pt3 = NULL;
+
+ &nbsp; &nbsp;while (1) {
+
+ &nbsp; &nbsp; &nbsp;pt1 = strtok_r(str, ",", &saveptr1);
+ &nbsp; &nbsp; &nbsp;if (pt1 == NULL) break;
+ &nbsp; &nbsp; &nbsp;str = NULL;
+ &nbsp; &nbsp; &nbsp;save_pt1 = strdup(pt1);
+
+ &nbsp; &nbsp; &nbsp;pt2 = strtok_r(pt1, "-", &saveptr2);
+ &nbsp; &nbsp; &nbsp;pt3 = strtok_r(NULL, "-", &saveptr2);
+
+ &nbsp; &nbsp; &nbsp;struct vmrange* n = calloc(1, sizeof(struct vmrange));
+ &nbsp; &nbsp; &nbsp;n->next = afl_instr_code;
+
+ &nbsp; &nbsp; &nbsp;if (pt3 == NULL) { // filename
+ &nbsp; &nbsp; &nbsp; &nbsp;have_names = 1;
+ &nbsp; &nbsp; &nbsp; &nbsp;n->start = (target_ulong)-1;
+ &nbsp; &nbsp; &nbsp; &nbsp;n->end = 0;
+ &nbsp; &nbsp; &nbsp; &nbsp;n->name = save_pt1;
+ &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp;n->start = strtoull(pt2, NULL, 16);
+ &nbsp; &nbsp; &nbsp; &nbsp;n->end = strtoull(pt3, NULL, 16);
+ &nbsp; &nbsp; &nbsp; &nbsp;if (n->start && n->end) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->name = NULL;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;free(save_pt1);
+ &nbsp; &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;have_names = 1;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->start = (target_ulong)-1;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->end = 0;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->name = save_pt1;
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp; &nbsp;afl_instr_code = n;
+
+ &nbsp; &nbsp;}
+ &nbsp;}
+
+ &nbsp;if (getenv("AFL_QEMU_EXCLUDE_RANGES")) {
+ &nbsp; &nbsp;char *str = getenv("AFL_QEMU_EXCLUDE_RANGES");
+ &nbsp; &nbsp;char *saveptr1, *saveptr2 = NULL, *save_pt1;
+ &nbsp; &nbsp;char *pt1, *pt2, *pt3 = NULL;
+
+ &nbsp; &nbsp;while (1) {
+
+ &nbsp; &nbsp; &nbsp;pt1 = strtok_r(str, ",", &saveptr1);
+ &nbsp; &nbsp; &nbsp;if (pt1 == NULL) break;
+ &nbsp; &nbsp; &nbsp;str = NULL;
+ &nbsp; &nbsp; &nbsp;save_pt1 = strdup(pt1);
+
+ &nbsp; &nbsp; &nbsp;pt2 = strtok_r(pt1, "-", &saveptr2);
+ &nbsp; &nbsp; &nbsp;pt3 = strtok_r(NULL, "-", &saveptr2);
+
+ &nbsp; &nbsp; &nbsp;struct vmrange* n = calloc(1, sizeof(struct vmrange));
+ &nbsp; &nbsp; &nbsp;n->exclude = true; // These are "exclusion" regions.
+ &nbsp; &nbsp; &nbsp;n->next = afl_instr_code;
+
+ &nbsp; &nbsp; &nbsp;if (pt3 == NULL) { // filename
+ &nbsp; &nbsp; &nbsp; &nbsp;have_names = 1;
+ &nbsp; &nbsp; &nbsp; &nbsp;n->start = (target_ulong)-1;
+ &nbsp; &nbsp; &nbsp; &nbsp;n->end = 0;
+ &nbsp; &nbsp; &nbsp; &nbsp;n->name = save_pt1;
+ &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp;n->start = strtoull(pt2, NULL, 16);
+ &nbsp; &nbsp; &nbsp; &nbsp;n->end = strtoull(pt3, NULL, 16);
+ &nbsp; &nbsp; &nbsp; &nbsp;if (n->start && n->end) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->name = NULL;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;free(save_pt1);
+ &nbsp; &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;have_names = 1;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->start = (target_ulong)-1;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->end = 0;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->name = save_pt1;
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp; &nbsp;afl_instr_code = n;
+
+ &nbsp; &nbsp;}
+ &nbsp;}
+
+ &nbsp;if (have_names) {
+ &nbsp; &nbsp;GSList *map_info = read_self_maps();
+ &nbsp; &nbsp;for (GSList *s = map_info; s; s = g_slist_next(s)) {
+ &nbsp; &nbsp; &nbsp;MapInfo *e = (MapInfo *) s->data;
+
+ &nbsp; &nbsp; &nbsp;if (h2g_valid(e->start)) {
+ &nbsp; &nbsp; &nbsp; &nbsp;unsigned long min = e->start;
+ &nbsp; &nbsp; &nbsp; &nbsp;unsigned long max = e->end;
+ &nbsp; &nbsp; &nbsp; &nbsp;int flags = page_get_flags(h2g(min));
+
+ &nbsp; &nbsp; &nbsp; &nbsp;max = h2g_valid(max - 1) ? max : (uintptr_t) AFL_G2H(GUEST_ADDR_MAX) + 1;
+
+ &nbsp; &nbsp; &nbsp; &nbsp;if (page_check_range(h2g(min), max - min, flags) == -1) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;continue;
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp; &nbsp; &nbsp;// Now that we have a valid guest address region, compare its
+ &nbsp; &nbsp; &nbsp; &nbsp;// name against the names we care about:
+ &nbsp; &nbsp; &nbsp; &nbsp;target_ulong gmin = h2g(min);
+ &nbsp; &nbsp; &nbsp; &nbsp;target_ulong gmax = h2g(max);
+
+ &nbsp; &nbsp; &nbsp; &nbsp;struct vmrange* n = afl_instr_code;
+ &nbsp; &nbsp; &nbsp; &nbsp;while (n) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (n->name && strstr(e->path, n->name)) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (gmin < n->start) n->start = gmin;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (gmax > n->end) n->end = gmax;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n = n->next;
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;free_self_maps(map_info);
+ &nbsp;}
+
+ &nbsp;if (getenv("AFL_DEBUG") && afl_instr_code) {
+ &nbsp; &nbsp;struct vmrange* n = afl_instr_code;
+ &nbsp; &nbsp;while (n) {
+ &nbsp; &nbsp; &nbsp;if (n->exclude) {
+ &nbsp; &nbsp; &nbsp; &nbsp;fprintf(stderr, "Exclude range: 0x%lx-0x%lx (%s)\n",
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(unsigned long)n->start, (unsigned long)n->end,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->name ? n->name : "<noname>");
+ &nbsp; &nbsp; &nbsp;} else {
+ &nbsp; &nbsp; &nbsp; &nbsp;fprintf(stderr, "Instrument range: 0x%lx-0x%lx (%s)\n",
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(unsigned long)n->start, (unsigned long)n->end,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;n->name ? n->name : "<noname>");
+ &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp;n = n->next;
+ &nbsp; &nbsp;}
+ &nbsp;}
+
+ &nbsp;/* Maintain for compatibility */
+ &nbsp;if (getenv("AFL_QEMU_COMPCOV")) { afl_compcov_level = 1; }
+ &nbsp;if (getenv("AFL_COMPCOV_LEVEL")) {
+
+ &nbsp; &nbsp;afl_compcov_level = atoi(getenv("AFL_COMPCOV_LEVEL"));
+
+ &nbsp;}
+
+ &nbsp;/* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
+ &nbsp; &nbsp; not entirely sure what is the cause. This disables that
+ &nbsp; &nbsp; behaviour, and seems to work alright? */
+
+ &nbsp;rcu_disable_atfork();
+
+ &nbsp;if (getenv("AFL_QEMU_PERSISTENT_HOOK")) {
+
+#ifdef&nbsp;AFL_QEMU_STATIC_BUILD
+
+ &nbsp; &nbsp;fprintf(stderr,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"[AFL] ERROR: you cannot use AFL_QEMU_PERSISTENT_HOOK when "
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"afl-qemu-trace is static\n");
+ &nbsp; &nbsp;exit(1);
+
+#else
+
+ &nbsp; &nbsp;persistent_save_gpr = 1;
+
+ &nbsp; &nbsp;void *plib = dlopen(getenv("AFL_QEMU_PERSISTENT_HOOK"), RTLD_NOW);
+ &nbsp; &nbsp;if (!plib) {
+
+ &nbsp; &nbsp; &nbsp;fprintf(stderr, "[AFL] ERROR: invalid AFL_QEMU_PERSISTENT_HOOK=%s - %s\n",
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;getenv("AFL_QEMU_PERSISTENT_HOOK"),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dlerror());
+ &nbsp; &nbsp; &nbsp;exit(1);
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;int (*afl_persistent_hook_init_ptr)(void) =
+ &nbsp; &nbsp; &nbsp; &nbsp;dlsym(plib, "afl_persistent_hook_init");
+ &nbsp; &nbsp;if (afl_persistent_hook_init_ptr)
+ &nbsp; &nbsp; &nbsp;sharedmem_fuzzing = afl_persistent_hook_init_ptr();
+
+ &nbsp; &nbsp;afl_persistent_hook_ptr = dlsym(plib, "afl_persistent_hook");
+ &nbsp; &nbsp;if (!afl_persistent_hook_ptr) {
+
+ &nbsp; &nbsp; &nbsp;fprintf(stderr,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"[AFL] ERROR: failed to find the function "
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"\"afl_persistent_hook\" in %s\n",
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;getenv("AFL_QEMU_PERSISTENT_HOOK"));
+ &nbsp; &nbsp; &nbsp;exit(1);
+
+ &nbsp; &nbsp;}
+
+#endif
+
+ &nbsp;}
+
+ &nbsp;if (__afl_cmp_map) return; // no persistent for cmplog
+
+ &nbsp;is_persistent = getenv("AFL_QEMU_PERSISTENT_ADDR") != NULL;
+
+ &nbsp;if (is_persistent)
+ &nbsp; &nbsp;afl_persistent_addr = strtoll(getenv("AFL_QEMU_PERSISTENT_ADDR"), NULL, 0);
+
+ &nbsp;if (getenv("AFL_QEMU_PERSISTENT_RET"))
+ &nbsp; &nbsp;afl_persistent_ret_addr =
+ &nbsp; &nbsp; &nbsp; &nbsp;strtoll(getenv("AFL_QEMU_PERSISTENT_RET"), NULL, 0);
+ &nbsp;/* If AFL_QEMU_PERSISTENT_RET is not specified patch the return addr */
+
+ &nbsp;if (getenv("AFL_QEMU_PERSISTENT_GPR")) persistent_save_gpr = 1;
+ &nbsp;if (getenv("AFL_QEMU_PERSISTENT_MEM"))
+ &nbsp; &nbsp;persistent_memory = 1;
+
+ &nbsp;if (getenv("AFL_QEMU_PERSISTENT_RETADDR_OFFSET"))
+ &nbsp; &nbsp;persisent_retaddr_offset =
+ &nbsp; &nbsp; &nbsp; &nbsp;strtoll(getenv("AFL_QEMU_PERSISTENT_RETADDR_OFFSET"), NULL, 0);
+
+ &nbsp;if (getenv("AFL_QEMU_PERSISTENT_CNT"))
+ &nbsp; &nbsp;afl_persistent_cnt = strtoll(getenv("AFL_QEMU_PERSISTENT_CNT"), NULL, 0);
+ &nbsp;else
+ &nbsp; &nbsp;afl_persistent_cnt = 0;
+
+ &nbsp;if (getenv("AFL_QEMU_PERSISTENT_EXITS")) persistent_exits = 1;
+
+ &nbsp;// TODO persistent exits for other archs not x86
+ &nbsp;// TODO persistent mode for other archs not x86
+ &nbsp;// TODO cmplog rtn for arm
+
+ &nbsp;if (getenv("AFL_QEMU_SNAPSHOT")) {
+
+ &nbsp; &nbsp;is_persistent = 1;
+ &nbsp; &nbsp;persistent_save_gpr = 1;
+ &nbsp; &nbsp;persistent_memory = 1;
+ &nbsp; &nbsp;persistent_exits = 1;
+
+ &nbsp; &nbsp;if (afl_persistent_addr == 0)
+ &nbsp; &nbsp; &nbsp;afl_persistent_addr = strtoll(getenv("AFL_QEMU_SNAPSHOT"), NULL, 0);
+
+ &nbsp;}
+
+ &nbsp;if (persistent_memory && afl_snapshot_init() >= 0)
+ &nbsp; &nbsp;lkm_snapshot = 1;
+
+ &nbsp;if (getenv("AFL_DEBUG")) {
+ &nbsp; &nbsp;if (is_persistent)
+ &nbsp; &nbsp; &nbsp;fprintf(stderr, "Persistent: 0x%lx [0x%lx] %s%s%s\n",
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(unsigned long)afl_persistent_addr,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(unsigned long)afl_persistent_ret_addr,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(persistent_save_gpr ? "gpr ": ""),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(persistent_memory ? "mem ": ""),
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(persistent_exits ? "exits ": ""));
+ &nbsp;}
+
+ &nbsp;qemu_ijon_init();
+
+}
+
+/* Fork server logic, invoked once we hit _start. */
+
+void afl_forkserver(CPUState *cpu) {
+
+ &nbsp;if (forkserver_installed == 1) return;
+ &nbsp;forkserver_installed = 1;
+
+ &nbsp;if (getenv("AFL_QEMU_DEBUG_MAPS")) open_self_maps(cpu->env_ptr, 1);
+
+ &nbsp;u32 __afl_old_forkserver = 0;
+ &nbsp;pid_t child_pid;
+ &nbsp;int &nbsp; t_fd[2];
+ &nbsp;u8 &nbsp; &nbsp;child_stopped = 0;
+ &nbsp;u32 &nbsp; was_killed;
+ &nbsp;u32 version = 0x41464c00 + FS_NEW_VERSION_MAX;
+ &nbsp;u32 tmp = version ^ 0xffffffff, status2, status = version;
+ &nbsp;u8 *msg = (u8 *)&status;
+ &nbsp;u8 *reply = (u8 *)&status2;
+
+ &nbsp;if (getenv("AFL_DEBUG"))
+ &nbsp; &nbsp;fprintf(stderr, "Debug: Sending status 0x%08x\n", status);
+
+ &nbsp;if (getenv("AFL_OLD_FORKSERVER")) {
+
+ &nbsp; &nbsp;__afl_old_forkserver = 1;
+ &nbsp; &nbsp;status = 0;
+
+ &nbsp; &nbsp;fprintf(stderr, "The current version of afl++ qemu mode "
+ &nbsp; &nbsp; &nbsp;"supports forkserver v1, but afl-fuzz still retains "
+ &nbsp; &nbsp; &nbsp;"support for the old forkserver (qemu) version\n");
+
+ &nbsp;}
+
+ &nbsp;/* Tell the parent that we're alive. If the parent doesn't want
+ &nbsp; &nbsp; to talk, assume that we're not running in forkserver mode. */
+
+ &nbsp;if (write(FORKSRV_FD + 1, msg, 4) != 4) return;
+
+ &nbsp;afl_forksrv_pid = getpid();
+
+ &nbsp;int first_run = 1;
+
+ &nbsp;if (!__afl_old_forkserver) {
+
+ &nbsp; &nbsp;if (read(FORKSRV_FD, reply, 4) != 4) { _exit(1); }
+ &nbsp; &nbsp;if (tmp != status2) {
+
+ &nbsp; &nbsp; &nbsp;fprintf(stderr, "wrong forkserver message from AFL++ tool");
+ &nbsp; &nbsp; &nbsp;_exit(1);
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;// send the set/requested options to forkserver
+ &nbsp; &nbsp;status = FS_NEW_OPT_MAPSIZE; &nbsp;// we always send the map size
+ &nbsp; &nbsp;if (lkm_snapshot) status |= FS_OPT_SNAPSHOT;
+ &nbsp; &nbsp;if (sharedmem_fuzzing) status |= FS_NEW_OPT_SHDMEM_FUZZ;
+
+ &nbsp; &nbsp;u32 __afl_map_size = MAP_SIZE;
+
+ &nbsp; &nbsp;if (use_ijon) {
+
+ &nbsp; &nbsp; &nbsp;__afl_map_size = (((__afl_map_size + 63) >> 6) << 6);
+ &nbsp; &nbsp; &nbsp;__afl_map_size += MAP_SIZE_IJON_MAP + MAP_SIZE_IJON_BYTES;
+
+ &nbsp; &nbsp; &nbsp;ijon_map_ptr = afl_area_ptr + MAP_SIZE;
+ &nbsp; &nbsp; &nbsp;ijon_max_ptr = (uint64_t*)(ijon_map_ptr + MAP_SIZE_IJON_MAP);
+
+ &nbsp; &nbsp; &nbsp;status |= FS_OPT_IJON;
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;if (write(FORKSRV_FD + 1, msg, 4) != 4) {
+
+ &nbsp; &nbsp; &nbsp;errno = 0;
+ &nbsp; &nbsp; &nbsp;_exit(1);
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;// Now send the parameters for the set options, increasing by option number
+
+ &nbsp; &nbsp;// FS_NEW_OPT_MAPSIZE - we always send the map size
+ &nbsp; &nbsp;status = __afl_map_size;
+ &nbsp; &nbsp;if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); }
+
+ &nbsp; &nbsp;// send welcome message as final message
+ &nbsp; &nbsp;status = version;
+ &nbsp; &nbsp;if (write(FORKSRV_FD + 1, msg, 4) != 4) { _exit(1); }
+
+ &nbsp;}
+
+ &nbsp;// END forkserver handshake
+
+ &nbsp;if (sharedmem_fuzzing) { afl_map_shm_fuzz(); }
+
+ &nbsp;/* All right, let's await orders... */
+
+ &nbsp;while (1) {
+
+ &nbsp; &nbsp;/* Whoops, parent dead? */
+
+ &nbsp; &nbsp;if (read(FORKSRV_FD, &was_killed, 4) != 4) exit(2);
+
+ &nbsp; &nbsp;/* If we stopped the child in persistent mode, but there was a race
+ &nbsp; &nbsp; &nbsp; condition and afl-fuzz already issued SIGKILL, write off the old
+ &nbsp; &nbsp; &nbsp; process. */
+
+ &nbsp; &nbsp;if (child_stopped && was_killed) {
+
+ &nbsp; &nbsp; &nbsp;child_stopped = 0;
+ &nbsp; &nbsp; &nbsp;if (waitpid(child_pid, &status, 0) < 0) exit(8);
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;if (!child_stopped) {
+
+ &nbsp; &nbsp; &nbsp;/* Establish a channel with child to grab translation commands. We'll
+ &nbsp; &nbsp; &nbsp; read from t_fd[0], child will write to TSL_FD. */
+
+ &nbsp; &nbsp; &nbsp;if (pipe(t_fd) || dup2(t_fd[1], TSL_FD) < 0) exit(3);
+ &nbsp; &nbsp; &nbsp;close(t_fd[1]);
+
+ &nbsp; &nbsp; &nbsp;child_pid = fork();
+ &nbsp; &nbsp; &nbsp;if (child_pid < 0) exit(4);
+
+ &nbsp; &nbsp; &nbsp;if (!child_pid) {
+
+ &nbsp; &nbsp; &nbsp; &nbsp;/* Child process. Close descriptors and run free. */
+
+ &nbsp; &nbsp; &nbsp; &nbsp;afl_fork_child = 1;
+ &nbsp; &nbsp; &nbsp; &nbsp;close(FORKSRV_FD);
+ &nbsp; &nbsp; &nbsp; &nbsp;close(FORKSRV_FD + 1);
+ &nbsp; &nbsp; &nbsp; &nbsp;close(t_fd[0]);
+ &nbsp; &nbsp; &nbsp; &nbsp;return;
+
+ &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp; &nbsp;/* Parent. */
+
+ &nbsp; &nbsp; &nbsp;close(TSL_FD);
+
+ &nbsp; &nbsp;} else {
+
+ &nbsp; &nbsp; &nbsp;/* Special handling for persistent mode: if the child is alive but
+ &nbsp; &nbsp; &nbsp; &nbsp; currently stopped, simply restart it with SIGCONT. */
+
+ &nbsp; &nbsp; &nbsp;kill(child_pid, SIGCONT);
+ &nbsp; &nbsp; &nbsp;child_stopped = 0;
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;/* Parent. */
+
+ &nbsp; &nbsp;if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(5);
+
+ &nbsp; &nbsp;/* Collect translation requests until child dies and closes the pipe. */
+
+ &nbsp; &nbsp;afl_wait_tsl(cpu, t_fd[0]);
+
+ &nbsp; &nbsp;/* Get and relay exit status to parent. */
+
+ &nbsp; &nbsp;if (waitpid(child_pid, &status, is_persistent ? WUNTRACED : 0) < 0) exit(6);
+
+ &nbsp; &nbsp;/* In persistent mode, the child stops itself with SIGSTOP to indicate
+ &nbsp; &nbsp; &nbsp; a successful run. In this case, we want to wake it up without forking
+ &nbsp; &nbsp; &nbsp; again. */
+
+ &nbsp; &nbsp;if (WIFSTOPPED(status))
+ &nbsp; &nbsp; &nbsp;child_stopped = 1;
+ &nbsp; &nbsp;else if (unlikely(first_run && is_persistent)) {
+
+ &nbsp; &nbsp; &nbsp;fprintf(stderr, "[AFL] ERROR: no persistent iteration executed\n");
+ &nbsp; &nbsp; &nbsp;exit(12); &nbsp;// Persistent is wrong
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;first_run = 0;
+
+ &nbsp; &nbsp;if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(7);
+
+ &nbsp;}
+
+}
+
+/* A simplified persistent mode handler, used as explained in
+ * llvm_mode/README.md. */
+
+static u32 cycle_cnt;
+
+void afl_persistent_iter(CPUArchState *env) {
+
+ &nbsp;static struct afl_tsl exit_cmd_tsl;
+
+ &nbsp;if (!afl_persistent_cnt || --cycle_cnt) {
+
+ &nbsp; &nbsp;if (persistent_memory) restore_memory_snapshot();
+
+ &nbsp; &nbsp;if (persistent_save_gpr && !afl_persistent_hook_ptr) {
+ &nbsp; &nbsp; &nbsp;afl_restore_regs(&saved_regs, env);
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;if (!disable_caching) {
+
+ &nbsp; &nbsp; &nbsp;memset(&exit_cmd_tsl, 0, sizeof(struct afl_tsl));
+ &nbsp; &nbsp; &nbsp;exit_cmd_tsl.tb.pc = (target_ulong)(-1);
+
+ &nbsp; &nbsp; &nbsp;if (write(TSL_FD, &exit_cmd_tsl, sizeof(struct afl_tsl)) !=
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;sizeof(struct afl_tsl)) {
+
+ &nbsp; &nbsp; &nbsp; &nbsp;/* Exit the persistent loop on pipe error */
+ &nbsp; &nbsp; &nbsp; &nbsp;afl_area_ptr = dummy;
+ &nbsp; &nbsp; &nbsp; &nbsp;exit(0);
+
+ &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;// TODO use only pipe
+ &nbsp; &nbsp;raise(SIGSTOP);
+
+
+ &nbsp; &nbsp;// now we have shared_buf updated and ready to use
+ &nbsp; &nbsp;if (persistent_save_gpr && afl_persistent_hook_ptr) {
+
+ &nbsp; &nbsp; &nbsp;struct api_regs hook_regs = saved_regs;
+ &nbsp; &nbsp; &nbsp;afl_persistent_hook_ptr(&hook_regs, guest_base, shared_buf,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*shared_buf_len);
+ &nbsp; &nbsp; &nbsp;afl_restore_regs(&hook_regs, env);
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;afl_area_ptr[0] = 1;
+ &nbsp; &nbsp;afl_prev_loc = 0;
+
+ &nbsp;} else {
+
+ &nbsp; &nbsp;afl_area_ptr = dummy;
+ &nbsp; &nbsp;exit(0);
+
+ &nbsp;}
+
+}
+
+void afl_persistent_loop(CPUArchState *env) {
+
+ &nbsp;if (!afl_fork_child) return;
+
+ &nbsp;if (persistent_first_pass) {
+
+ &nbsp; &nbsp;/* Make sure that every iteration of __AFL_LOOP() starts with a clean slate.
+ &nbsp; &nbsp; &nbsp; On subsequent calls, the parent will take care of that, but on the first
+ &nbsp; &nbsp; &nbsp; iteration, it's our job to erase any trace of whatever happened
+ &nbsp; &nbsp; &nbsp; before the loop. */
+
+ &nbsp; &nbsp;if (is_persistent) {
+
+ &nbsp; &nbsp; &nbsp;memset(afl_area_ptr, 0, MAP_SIZE);
+ &nbsp; &nbsp; &nbsp;afl_area_ptr[0] = 1;
+ &nbsp; &nbsp; &nbsp;afl_prev_loc = 0;
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;if (persistent_memory) collect_memory_snapshot();
+
+ &nbsp; &nbsp;if (persistent_save_gpr) {
+
+ &nbsp; &nbsp; &nbsp;afl_save_regs(&saved_regs, env);
+
+ &nbsp; &nbsp; &nbsp;if (afl_persistent_hook_ptr) {
+
+ &nbsp; &nbsp; &nbsp; &nbsp;struct api_regs hook_regs = saved_regs;
+ &nbsp; &nbsp; &nbsp; &nbsp;afl_persistent_hook_ptr(&hook_regs, guest_base, shared_buf,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*shared_buf_len);
+ &nbsp; &nbsp; &nbsp; &nbsp;afl_restore_regs(&hook_regs, env);
+
+ &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;cycle_cnt = afl_persistent_cnt;
+ &nbsp; &nbsp;persistent_first_pass = 0;
+ &nbsp; &nbsp;persistent_stack_offset = TARGET_LONG_BITS / 8;
+
+ &nbsp; &nbsp;return;
+
+ &nbsp;}
+
+ &nbsp;if (is_persistent) {
+
+ &nbsp; &nbsp;afl_persistent_iter(env);
+
+ &nbsp;}
+
+}
+
+/* This code is invoked whenever QEMU decides that it doesn't have a
+ &nbsp; translation of a particular block and needs to compute it, or when it
+ &nbsp; decides to chain two TBs together. When this happens, we tell the parent to
+ &nbsp; mirror the operation, so that the next fork() has a cached copy. */
+
+static void afl_request_tsl(target_ulong pc, target_ulong cb, uint32_t flags,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;uint32_t cf_mask, TranslationBlock *last_tb,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;int tb_exit) {
+
+ &nbsp;if (disable_caching) return;
+
+ &nbsp;struct afl_tsl t;
+
+ &nbsp;if (!afl_fork_child) return;
+
+ &nbsp;t.tb.pc = pc;
+ &nbsp;t.tb.cs_base = cb;
+ &nbsp;t.tb.flags = flags;
+ &nbsp;t.tb.cf_mask = cf_mask;
+ &nbsp;t.is_chain = (last_tb != NULL);
+
+ &nbsp;if (t.is_chain) {
+
+ &nbsp; &nbsp;t.chain.last_tb.pc = last_tb->pc;
+ &nbsp; &nbsp;t.chain.last_tb.cs_base = last_tb->cs_base;
+ &nbsp; &nbsp;t.chain.last_tb.flags = last_tb->flags;
+ &nbsp; &nbsp;t.chain.cf_mask = cf_mask;
+ &nbsp; &nbsp;t.chain.tb_exit = tb_exit;
+
+ &nbsp;}
+
+ &nbsp;if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
+ &nbsp; &nbsp;return;
+
+}
+
+static inline TranslationBlock *
+afl_tb_lookup(CPUState *cpu, target_ulong pc, target_ulong cs_base,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uint32_t flags, uint32_t cf_mask)
+{
+ &nbsp; &nbsp;TranslationBlock *tb;
+ &nbsp; &nbsp;uint32_t hash;
+
+ &nbsp; &nbsp;hash = tb_jmp_cache_hash_func(pc);
+ &nbsp; &nbsp;tb = qatomic_rcu_read(&cpu->tb_jmp_cache[hash]);
+
+ &nbsp; &nbsp;cf_mask &= ~CF_CLUSTER_MASK;
+ &nbsp; &nbsp;cf_mask |= cpu->cluster_index << CF_CLUSTER_SHIFT;
+
+ &nbsp; &nbsp;if (likely(tb &&
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tb->pc == pc &&
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tb->cs_base == cs_base &&
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tb->flags == flags &&
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tb->trace_vcpu_dstate == *cpu->trace_dstate &&
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (tb_cflags(tb) & (CF_HASH_MASK | CF_INVALID)) == cf_mask)) {
+ &nbsp; &nbsp; &nbsp; &nbsp;return tb;
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
+ &nbsp; &nbsp;if (tb == NULL) {
+ &nbsp; &nbsp; &nbsp; &nbsp;return NULL;
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;qatomic_set(&cpu->tb_jmp_cache[hash], tb);
+ &nbsp; &nbsp;return tb;
+}
+
+/* This is the other side of the same channel. Since timeouts are handled by
+ &nbsp; afl-fuzz simply killing the child, we can just wait until the pipe breaks. */
+
+static void afl_wait_tsl(CPUState *cpu, int fd) {
+
+ &nbsp;struct afl_tsl t;
+ &nbsp;TranslationBlock *tb, *last_tb;
+
+ &nbsp;if (disable_caching) return;
+
+ &nbsp;while (1) {
+
+ &nbsp; &nbsp;u8 invalid_pc = 0;
+
+ &nbsp; &nbsp;/* Broken pipe means it's time to return to the fork server routine. */
+
+ &nbsp; &nbsp;if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl)) break;
+
+ &nbsp; &nbsp;/* Exit command for persistent */
+
+ &nbsp; &nbsp;if (t.tb.pc == (target_ulong)(-1)) return;
+
+ &nbsp; &nbsp;tb = afl_tb_lookup(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, t.tb.cf_mask);
+
+ &nbsp; &nbsp;if (!tb) {
+
+ &nbsp; &nbsp; &nbsp;/* The child may request to transate a block of memory that is not
+ &nbsp; &nbsp; &nbsp; &nbsp; mapped in the parent (e.g. jitted code or dlopened code).
+ &nbsp; &nbsp; &nbsp; &nbsp; This causes a SIGSEV in gen_intermediate_code() and associated
+ &nbsp; &nbsp; &nbsp; &nbsp; subroutines. We simply avoid caching of such blocks. */
+
+ &nbsp; &nbsp; &nbsp;if (is_valid_addr(t.tb.pc)) {
+
+ &nbsp; &nbsp; &nbsp; &nbsp;mmap_lock();
+ &nbsp; &nbsp; &nbsp; &nbsp;tb = tb_gen_code(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, t.tb.cf_mask);
+ &nbsp; &nbsp; &nbsp; &nbsp;mmap_unlock();
+
+ &nbsp; &nbsp; &nbsp;} else {
+
+ &nbsp; &nbsp; &nbsp; &nbsp;invalid_pc = 1;
+
+ &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;if (t.is_chain && !invalid_pc) {
+
+ &nbsp; &nbsp; &nbsp;last_tb = afl_tb_lookup(cpu, t.chain.last_tb.pc,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.chain.last_tb.cs_base,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.chain.last_tb.flags,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.chain.cf_mask);
+#define&nbsp;TB_JMP_RESET_OFFSET_INVALID 0xffff
+ &nbsp; &nbsp; &nbsp; &nbsp;if (last_tb && (last_tb->jmp_reset_offset[t.chain.tb_exit] !=
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TB_JMP_RESET_OFFSET_INVALID)) {
+
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tb_add_jump(last_tb, t.chain.tb_exit, tb);
+
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+
+ &nbsp; &nbsp;}
+
+ &nbsp;}
+
+ &nbsp;close(fd);
+
+}
+
&nbsp;/* -icount align implementation. */

&nbsp;typedef struct SyncClocks {
@@ -946,6 +2521,7 @@ static int __attribute__((noinline))
&nbsp;cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
&nbsp;{
&nbsp; &nbsp; &nbsp;int ret;
+ &nbsp; &nbsp;bool was_translated = false, was_chained = false;

&nbsp; &nbsp; &nbsp;/* if an exception is pending, we execute it here */
&nbsp; &nbsp; &nbsp;while (!cpu_handle_exception(cpu, &ret)) {
@@ -985,6 +2561,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;mmap_lock();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;was_translated = true;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;mmap_unlock();

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/*
@@ -1011,6 +2588,11 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* See if we can patch the calling TB. */
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (last_tb) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tb_add_jump(last_tb, tb_exit, tb);
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;was_chained = true;
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (was_translated || was_chained) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;afl_request_tsl(s.pc, s.cs_base, s.flags, s.cf_mask,
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;was_chained ? last_tb : NULL, tb_exit);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;cpu_loop_exec_tb(cpu, tb, pc, &last_tb, &tb_exit);

cpu-exec.c 中的 cpu_exec_loop 函数为 QEMU 仿真的核心函数,调用流程大致如下:

  • linux-user/main.c 的 main 函数初始化结束后,调用cpu_loop,开始执行目标程序代码。
  • cpu_loop 函数位于linux-user/{arch}/cpu_loop.c 文件中。
  • cpu_loop调用accel/tcg/cpu-exec.c 文件中的cpu_exec函数,进行一些初始化后,执行到cpu_exec_setjmp函数。
  • cpu_exec_setjmp 函数的作用相当于使用 try … catch … 执行 cpu_exec_loop 函数。
  • cpu_exec_loop 函数执行代码块中的 Host 机器码,若不存在则开始翻译。
  • 在cpu_exec_loop 函数中,调用tb_lookup根据当前 pc 地址去搜索对应的代码块。如果存在,则直接调用cpu_loop_exec_tb去执行 Host 机器码。
  • 如果不存在对应的代码块,则调用tb_gen_code开始翻译。
  • 在tb_gen_code中调用到setjmp_gen_code函数。
  • 在setjmp_gen_code函数中,首先使用translate_code函数将Guest 指令翻译成 TCG IR。
  • translate_code函数位于target/{arch}/tcg/cpu.c 文件中。
  • 在翻译成 IR 指令后,再调用tcg_gen_code函数,把 IR 指令翻译成 Host 机器码。

AFL 在该文件中的 patch 是将IJON、snapshot、persistent 模式等功能集成进 QEMU 当中,如下所示:

加入覆盖率共享内存(SHM)映射 加入 AFL forkserver 支持 加入持久化模式(persistent mode)支持 加入持久化模式下 snapshot(快照)/ restore(恢复)功能 加入 IJON(高级指导型 fuzzing)支持 加入比较覆盖(CmpCov)支持 加入 AFL TB 链跟踪结构(afl_tb, afl_chain 等) 加入 sharedmem fuzzing 模式支持 加入对 shared memory / registers 的 IJON hook 支持 扫描并保存 /proc/self/maps 的内存区域 在翻译完指令后,会将翻译情况传输给父进程forkserver。

接下来是tcg目录下的代码。

1.tcg/tcg.c文件patch 内容如下所示:

diff --git a/tcg/tcg.c b/tcg/tcg.c
index b1a7465df2..964e91ee32 100644
--- a/tcg/tcg.c
+++ b/tcg/tcg.c
@@ -61,6 +61,8 @@
&nbsp;#include&nbsp;"user/guest-base.h"
&nbsp;#endif

+#include&nbsp;"qemuafl/common.h"
+
&nbsp;/* Forward declarations for functions declared in tcg-target.c.inc and
&nbsp; &nbsp; used here. */
&nbsp;static void tcg_target_init(TCGContext *s);
@@ -2435,6 +2437,17 @@ bool tcg_op_deposit_valid(TCGType type, unsigned ofs, unsigned len)
&nbsp; &nbsp; &nbsp;return TCG_TARGET_deposit_valid(type, ofs, len);
&nbsp;}

+void afl_gen_tcg_plain_call(void *func)
+{
+ &nbsp; &nbsp;TCGOp *op = tcg_emit_op(INDEX_op_call);
+
+ &nbsp; &nbsp;TCGOP_CALLO(op) = 0;
+
+ &nbsp; &nbsp;op->args[0] = (uintptr_t)func;
+ &nbsp; &nbsp;op->args[1] = 0;
+ &nbsp; &nbsp;TCGOP_CALLI(op) = 0;
+}
+
&nbsp;static TCGOp *tcg_op_alloc(TCGOpcode opc, unsigned nargs);

该部分 AFL patch 主要实现一个简单接口,允许在 TCG 翻译过程中插入对指定函数的纯粹函数调用。

2.tcg/tcg-op.c文件patch 内容如下所示:

diff&nbsp;--git a/tcg/tcg-op.c b/tcg/tcg-op.c
index e2e25ebf7d..e42766c20d&nbsp;100644
---&nbsp;a/tcg/tcg-op.c
+++&nbsp;b/tcg/tcg-op.c
@@&nbsp;-32,6+32,25&nbsp;@@
#include&nbsp;"trace/mem.h"
#include&nbsp;"exec/plugin-gen.h"

+#include&nbsp;"qemuafl/qasan-qemu.h"
+
+#define&nbsp;GEN_QASAN_OP(OP) \
+void&nbsp;qasan_gen_##OP(TCGv addr,int&nbsp;off){&nbsp;\
+&nbsp; \
+if(use_qasan&nbsp;&&&nbsp;cur_block_is_good)&nbsp;\
+&nbsp; &nbsp; gen_helper_qasan_##OP(cpu_env,&nbsp;addr);&nbsp;\
+&nbsp;\
+}
+
+GEN_QASAN_OP(load1)
+GEN_QASAN_OP(load2)
+GEN_QASAN_OP(load4)
+GEN_QASAN_OP(load8)
+GEN_QASAN_OP(store1)
+GEN_QASAN_OP(store2)
+GEN_QASAN_OP(store4)
+GEN_QASAN_OP(store8)
+

@@&nbsp;-2836,9+2857,18&nbsp;@@&nbsp;voidtcg_gen_qemu_ld_i32(TCGv_i32 val,&nbsp;TCGv addr,&nbsp;TCGArg idx,&nbsp;MemOp memop)
}

&nbsp; &nbsp; &nbsp;addr&nbsp;=plugin_prep_mem_callbacks(addr);
+
+switch(memop&nbsp;&&nbsp;MO_SIZE){
+case&nbsp;MO_64:qasan_gen_load8(addr,&nbsp;idx);break;
+case&nbsp;MO_32:qasan_gen_load4(addr,&nbsp;idx);break;
+case&nbsp;MO_16:qasan_gen_load2(addr,&nbsp;idx);break;
+case&nbsp;MO_8:qasan_gen_load1(addr,&nbsp;idx);break;
+default:qasan_gen_load4(addr,&nbsp;idx);break;
+}
+
gen_ldst_i32(INDEX_op_qemu_ld_i32,&nbsp;val,&nbsp;addr,&nbsp;memop,&nbsp;idx);
plugin_gen_mem_callbacks(addr,&nbsp;info);
-
+
if((orig_memop&nbsp;^&nbsp;memop)&&nbsp;MO_BSWAP){
switch(orig_memop&nbsp;&&nbsp;MO_SIZE){
case&nbsp;MO_16:
@@&nbsp;-2883,7+2913,20&nbsp;@@&nbsp;voidtcg_gen_qemu_st_i32(TCGv_i32 val,&nbsp;TCGv addr,&nbsp;TCGArg idx,&nbsp;MemOp memop)
}

&nbsp; &nbsp; &nbsp;addr&nbsp;=plugin_prep_mem_callbacks(addr);
-gen_ldst_i32(INDEX_op_qemu_st_i32,&nbsp;val,&nbsp;addr,&nbsp;memop,&nbsp;idx);
+
+switch(memop&nbsp;&&nbsp;MO_SIZE){
+case&nbsp;MO_64:qasan_gen_store8(addr,&nbsp;idx);break;
+case&nbsp;MO_32:qasan_gen_store4(addr,&nbsp;idx);break;
+case&nbsp;MO_16:qasan_gen_store2(addr,&nbsp;idx);break;
+case&nbsp;MO_8:qasan_gen_store1(addr,&nbsp;idx);break;
+default:qasan_gen_store4(addr,&nbsp;idx);break;
+}
+
+if(TCG_TARGET_HAS_qemu_st8_i32&nbsp;&&(memop&nbsp;&&nbsp;MO_SIZE)==&nbsp;MO_8){
+gen_ldst_i32(INDEX_op_qemu_st8_i32,&nbsp;val,&nbsp;addr,&nbsp;memop,&nbsp;idx);
+}else{
+gen_ldst_i32(INDEX_op_qemu_st_i32,&nbsp;val,&nbsp;addr,&nbsp;memop,&nbsp;idx);
+}
plugin_gen_mem_callbacks(addr,&nbsp;info);

if(swap){
@@&nbsp;-2921,6+2964,15&nbsp;@@&nbsp;voidtcg_gen_qemu_ld_i64(TCGv_i64 val,&nbsp;TCGv addr,&nbsp;TCGArg idx,&nbsp;MemOp memop)
}

&nbsp; &nbsp; &nbsp;addr&nbsp;=plugin_prep_mem_callbacks(addr);
+
+switch(memop&nbsp;&&nbsp;MO_SIZE){
+case&nbsp;MO_64:qasan_gen_load8(addr,&nbsp;idx);break;
+case&nbsp;MO_32:qasan_gen_load4(addr,&nbsp;idx);break;
+case&nbsp;MO_16:qasan_gen_load2(addr,&nbsp;idx);break;
+case&nbsp;MO_8:qasan_gen_load1(addr,&nbsp;idx);break;
+default:qasan_gen_load8(addr,&nbsp;idx);break;
+}
+
gen_ldst_i64(INDEX_op_qemu_ld_i64,&nbsp;val,&nbsp;addr,&nbsp;memop,&nbsp;idx);
plugin_gen_mem_callbacks(addr,&nbsp;info);

@@&nbsp;-2984,6+3036,15&nbsp;@@&nbsp;voidtcg_gen_qemu_st_i64(TCGv_i64 val,&nbsp;TCGv addr,&nbsp;TCGArg idx,&nbsp;MemOp memop)
}

&nbsp; &nbsp; &nbsp;addr&nbsp;=plugin_prep_mem_callbacks(addr);
+
+switch(memop&nbsp;&&nbsp;MO_SIZE){
+case&nbsp;MO_64:qasan_gen_store8(addr,&nbsp;idx);break;
+case&nbsp;MO_32:qasan_gen_store4(addr,&nbsp;idx);break;
+case&nbsp;MO_16:qasan_gen_store2(addr,&nbsp;idx);break;
+case&nbsp;MO_8:qasan_gen_store1(addr,&nbsp;idx);break;
+default:qasan_gen_store8(addr,&nbsp;idx);break;
+}
+
gen_ldst_i64(INDEX_op_qemu_st_i64,&nbsp;val,&nbsp;addr,&nbsp;memop,&nbsp;idx);
plugin_gen_mem_callbacks(addr,&nbsp;info);

这段代码是 QASan实现的核心部分。

简单来说,它的作用是:在 QEMU 将 Guest(客户机)的内存读写指令翻译成中间码(TCG Ops)时,强制插入一段“检查代码”。

最后就是target/{arch}/tcg目录下的代码。

1.target/mips/tcg/translate.c文件patch 内容如下所示:

diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c
index 78b848a6d9..c4d42da965 100644
--- a/target/mips/tcg/translate.c
+++ b/target/mips/tcg/translate.c
@@ -49,6 +49,27 @@
&nbsp;STUB_HELPER(cache, TCGv_env env, TCGv val, TCGv_i32 reg)
&nbsp;#endif

+/* MIPS_PATCH */
+#include&nbsp;"qemuafl/cpu-translate.h"
+
+/* MIPS_PATCH */
+#define&nbsp;AFL_QEMU_TARGET_MIPS_SNIPPET &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
+ &nbsp;if (is_persistent) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
+ &nbsp; &nbsp;if (ctx->base.pc_next == afl_persistent_addr) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
+ &nbsp; &nbsp; &nbsp;gen_helper_afl_persistent_routine(cpu_env); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
+ &nbsp; &nbsp; &nbsp;if (afl_persistent_ret_addr == 0 && !persistent_exits) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
+ &nbsp; &nbsp; &nbsp; &nbsp;tcg_gen_movi_tl(cpu_gpr[31], afl_persistent_addr); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
+ &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
+ &nbsp; &nbsp; &nbsp;if (!persistent_save_gpr) afl_gen_tcg_plain_call(&afl_persistent_loop); \
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
+ &nbsp; &nbsp;} else if (afl_persistent_ret_addr && &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ctx->base.pc_next == afl_persistent_ret_addr) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\
+ &nbsp; &nbsp; &nbsp;gen_goto_tb(ctx, 0, afl_persistent_addr); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
+ &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \
+ &nbsp;}
+
&nbsp;enum {
&nbsp; &nbsp; &nbsp;/* indirect opcode tables */
&nbsp; &nbsp; &nbsp;OPC_SPECIAL &nbsp;= (0x00 << 26),
@@ -1187,6 +1208,128 @@ static const char regnames_LO[][4] = {
&nbsp; &nbsp; &nbsp;"LO0", "LO1", "LO2", "LO3",
&nbsp;};

+/* MIPS_PATCH */
+void afl_save_regs(struct api_regs* r, CPUArchState *env) {
+ &nbsp; &nbsp;int i = 0;
+ &nbsp; &nbsp;int j = 0;
+ &nbsp; &nbsp;/* GP registers saving */
+ &nbsp; &nbsp;r->r0 = env->active_tc.gpr[0];
+ &nbsp; &nbsp;r->at = env->active_tc.gpr[1];
+ &nbsp; &nbsp;r->v0 = env->active_tc.gpr[2];
+ &nbsp; &nbsp;r->v1 = env->active_tc.gpr[3];
+ &nbsp; &nbsp;r->a0 = env->active_tc.gpr[4];
+ &nbsp; &nbsp;r->a1 = env->active_tc.gpr[5];
+ &nbsp; &nbsp;r->a2 = env->active_tc.gpr[6];
+ &nbsp; &nbsp;r->a3 = env->active_tc.gpr[7];
+ &nbsp; &nbsp;r->t0 = env->active_tc.gpr[8];
+ &nbsp; &nbsp;r->t1 = env->active_tc.gpr[9];
+ &nbsp; &nbsp;r->t2 = env->active_tc.gpr[10];
+ &nbsp; &nbsp;r->t3 = env->active_tc.gpr[11];
+ &nbsp; &nbsp;r->t4 = env->active_tc.gpr[12];
+ &nbsp; &nbsp;r->t5 = env->active_tc.gpr[13];
+ &nbsp; &nbsp;r->t6 = env->active_tc.gpr[14];
+ &nbsp; &nbsp;r->t7 = env->active_tc.gpr[15];
+ &nbsp; &nbsp;r->s0 = env->active_tc.gpr[16];
+ &nbsp; &nbsp;r->s1 = env->active_tc.gpr[17];
+ &nbsp; &nbsp;r->s2 = env->active_tc.gpr[18];
+ &nbsp; &nbsp;r->s3 = env->active_tc.gpr[19];
+ &nbsp; &nbsp;r->s4 = env->active_tc.gpr[20];
+ &nbsp; &nbsp;r->s5 = env->active_tc.gpr[21];
+ &nbsp; &nbsp;r->s6 = env->active_tc.gpr[22];
+ &nbsp; &nbsp;r->s7 = env->active_tc.gpr[23];
+ &nbsp; &nbsp;r->t8 = env->active_tc.gpr[24];
+ &nbsp; &nbsp;r->t9 = env->active_tc.gpr[25];
+ &nbsp; &nbsp;r->k0 = env->active_tc.gpr[26];
+ &nbsp; &nbsp;r->k1 = env->active_tc.gpr[27];
+ &nbsp; &nbsp;r->gp = env->active_tc.gpr[28];
+ &nbsp; &nbsp;r->sp = env->active_tc.gpr[29];
+ &nbsp; &nbsp;r->fp = env->active_tc.gpr[30];
+ &nbsp; &nbsp;r->ra = env->active_tc.gpr[31];
+ &nbsp; &nbsp;r->PC = env->active_tc.PC;
+#if&nbsp;defined(TARGET_MIPS64)
+ &nbsp; &nbsp;memcpy(r->gpr_hi, env->active_tc.gpr_hi, sizeof(r->gpr_hi));
+#endif
+ &nbsp; &nbsp;for (i = 0; i < MIPS_DSP_ACC; i++) {
+ &nbsp; &nbsp; &nbsp; &nbsp;r->HI[i] = env->active_tc.HI[i];
+ &nbsp; &nbsp; &nbsp; &nbsp;r->LO[i] = env->active_tc.LO[i];
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;/* FP registers saving */
+ &nbsp; &nbsp;for (i = 0; i < 32; i++) {
+ &nbsp; &nbsp; &nbsp; &nbsp;r->fpr[i].fd = env->active_fpu.fpr[i].fd;
+ &nbsp; &nbsp; &nbsp; &nbsp;for (j = 0; j < 2; j++) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;r->fpr[i].fs[j] = env->active_fpu.fpr[i].fs[j];
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp;r->fpr[i].d = env->active_fpu.fpr[i].d;
+ &nbsp; &nbsp; &nbsp; &nbsp;for (j = 0; j < 2; j++) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;r->fpr[i].w[j] = env->active_fpu.fpr[i].w[j];
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp;for (j = 0; j < MSA_WRLEN / 8; j++) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;r->fpr[i].wr.b[j] = env->active_fpu.fpr[i].wr.b[j];
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp;}
+}
+
+/* MIPS_PATCH */
+void afl_restore_regs(struct api_regs* r, CPUArchState *env) {
+ &nbsp; &nbsp;int i = 0;
+ &nbsp; &nbsp;int j = 0;
+ &nbsp; &nbsp;/* GP registers restoring */
+ &nbsp; &nbsp;env->active_tc.gpr[0] = r->r0;
+ &nbsp; &nbsp;env->active_tc.gpr[1] = r->at;
+ &nbsp; &nbsp;env->active_tc.gpr[2] = r->v0;
+ &nbsp; &nbsp;env->active_tc.gpr[3] = r->v1;
+ &nbsp; &nbsp;env->active_tc.gpr[4] = r->a0;
+ &nbsp; &nbsp;env->active_tc.gpr[5] = r->a1;
+ &nbsp; &nbsp;env->active_tc.gpr[6] = r->a2;
+ &nbsp; &nbsp;env->active_tc.gpr[7] = r->a3;
+ &nbsp; &nbsp;env->active_tc.gpr[8] = r->t0;
+ &nbsp; &nbsp;env->active_tc.gpr[9] = r->t1;
+ &nbsp; &nbsp;env->active_tc.gpr[10] = r->t2;
+ &nbsp; &nbsp;env->active_tc.gpr[11] = r->t3;
+ &nbsp; &nbsp;env->active_tc.gpr[12] = r->t4;
+ &nbsp; &nbsp;env->active_tc.gpr[13] = r->t5;
+ &nbsp; &nbsp;env->active_tc.gpr[14] = r->t6;
+ &nbsp; &nbsp;env->active_tc.gpr[15] = r->t7;
+ &nbsp; &nbsp;env->active_tc.gpr[16] = r->s0;
+ &nbsp; &nbsp;env->active_tc.gpr[17] = r->s1;
+ &nbsp; &nbsp;env->active_tc.gpr[18] = r->s2;
+ &nbsp; &nbsp;env->active_tc.gpr[19] = r->s3;
+ &nbsp; &nbsp;env->active_tc.gpr[20] = r->s4;
+ &nbsp; &nbsp;env->active_tc.gpr[21] = r->s5;
+ &nbsp; &nbsp;env->active_tc.gpr[22] = r->s6;
+ &nbsp; &nbsp;env->active_tc.gpr[23] = r->s7;
+ &nbsp; &nbsp;env->active_tc.gpr[24] = r->t8;
+ &nbsp; &nbsp;env->active_tc.gpr[25] = r->t9;
+ &nbsp; &nbsp;env->active_tc.gpr[26] = r->k0;
+ &nbsp; &nbsp;env->active_tc.gpr[27] = r->k1;
+ &nbsp; &nbsp;env->active_tc.gpr[28] = r->gp;
+ &nbsp; &nbsp;env->active_tc.gpr[29] = r->sp;
+ &nbsp; &nbsp;env->active_tc.gpr[30] = r->fp;
+ &nbsp; &nbsp;env->active_tc.gpr[31] = r->ra;
+ &nbsp; &nbsp;env->active_tc.PC = r->PC;
+#if&nbsp;defined(TARGET_MIPS64)
+ &nbsp; &nbsp;memcpy(env->active_tc.gpr_hi, r->gpr_hi, sizeof(r->gpr_hi));
+#endif
+ &nbsp; &nbsp;for (i = 0; i < MIPS_DSP_ACC; i++) {
+ &nbsp; &nbsp; &nbsp; &nbsp;env->active_tc.HI[i] = r->HI[i];
+ &nbsp; &nbsp; &nbsp; &nbsp;env->active_tc.LO[i] = r->LO[i];
+ &nbsp; &nbsp;}
+ &nbsp; &nbsp;/* FP registers restoring */
+ &nbsp; &nbsp;for (i = 0; i < 32; i++) {
+ &nbsp; &nbsp; &nbsp; &nbsp;env->active_fpu.fpr[i].fd = r->fpr[i].fd;
+ &nbsp; &nbsp; &nbsp; &nbsp;for (j = 0; j < 2; j++) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;env->active_fpu.fpr[i].fs[j] = r->fpr[i].fs[j];
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp;env->active_fpu.fpr[i].d = r->fpr[i].d;
+ &nbsp; &nbsp; &nbsp; &nbsp;for (j = 0; j < 2; j++) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;env->active_fpu.fpr[i].w[j] = r->fpr[i].w[j];
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp; &nbsp; &nbsp;for (j = 0; j < MSA_WRLEN / 8; j++) {
+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;env->active_fpu.fpr[i].wr.b[j] = r->fpr[i].wr.b[j];
+ &nbsp; &nbsp; &nbsp; &nbsp;}
+ &nbsp; &nbsp;}
+}
+
&nbsp;/* General purpose registers moves. */
&nbsp;void gen_load_gpr(TCGv t, int reg)
&nbsp;{
@@ -15138,6 +15281,9 @@ static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
&nbsp; &nbsp; &nbsp;int insn_bytes;
&nbsp; &nbsp; &nbsp;int is_slot;

+ &nbsp; &nbsp;/* MIPS_PATCH */
+ &nbsp; &nbsp;AFL_QEMU_TARGET_MIPS_SNIPPET
+
&nbsp; &nbsp; &nbsp;is_slot = ctx->hflags & MIPS_HFLAG_BMASK;
&nbsp; &nbsp; &nbsp;if (ctx->insn_flags & ISA_NANOMIPS32) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ctx->opcode = translator_lduw(env, &ctx->base, ctx->base.pc_next);

target/{arch}/tcg/translate.c 主要将目标程序的指令翻译成 TCG IR。AFL 对该文件的 patch 主要添加 AFL 持久化模式支持,包括持久化 fuzz 逻辑注入及 MIPS 完整寄存器状态的保存与恢复,使 AFL 能以高效循环方式 fuzz MIPS 程序。

由于测试案例使用 MIPS 架构,因此上述 patch 内容仅包含 MIPS 架构相关部分,其他架构暂未 patch。此外,由于暂未使用 QASAN 功能,许多 QASAN 相关 patch 亦暂未加入。

往 期 热 门

(点击图片跳转)

戳“阅读原文”更多精彩内容!


查看原文:《原创 Paper | AFL Fuzz QEMU 新版适配:深度解析 Patch 细节》

评论:0   参与:  2