Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save astarasikov/77b36ca66ff5920ebea595c78de8556a to your computer and use it in GitHub Desktop.
Save astarasikov/77b36ca66ff5920ebea595c78de8556a to your computer and use it in GitHub Desktop.
AFL QEMU mode for QEMU usermode
diff --git a/afl-fuzz.c b/afl-fuzz.c
index a4b0e15..1543857 100644
--- a/afl-fuzz.c
+++ b/afl-fuzz.c
@@ -6934,7 +6934,7 @@ EXP_ST void check_binary(u8* fname) {
" QEMU or Unicorn mode (-Q or -U). This is probably not what you want -\n"
" this setup will be slow and offer no practical benefits.\n");
- FATAL("Instrumentation found in -Q or -U mode");
+ //FATAL("Instrumentation found in -Q or -U mode");
}
@@ -7991,7 +7991,7 @@ int main(int argc, char** argv) {
start_time = get_cur_time();
- if (qemu_mode)
+ if (0 && qemu_mode)
use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
else
use_argv = argv + optind;
commit f897cda941206b3a89065a5986cad5fc263f23e4
Author: Alexander Tarasikov <alexander.tarasikov@gmail.com>
Date: Sat Aug 17 03:11:32 2019 +0300
HACK: port QEMU mode AFL support.
Currently requires some workarounds for AFL but seems to work.
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 45ef41ebb2..3815613635 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -36,6 +36,8 @@
#include "sysemu/cpus.h"
#include "sysemu/replay.h"
+#include "../../afl-qemu-cpu-inl.h"
+
/* -icount align implementation. */
typedef struct SyncClocks {
@@ -144,6 +146,8 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
int tb_exit;
uint8_t *tb_ptr = itb->tc.ptr;
+ AFL_QEMU_CPU_SNIPPET2;
+
qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc,
"Trace %d: %p ["
TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n",
@@ -215,6 +219,8 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
mmap_lock();
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base,
orig_tb->flags, cflags);
+ //AFL_QEMU_CPU_SNIPPET1;
+ //afl_request_tsl(orig_tb->pc, orig_tb->cs_base, orig_tb->flags);
tb->orig_tb = orig_tb;
mmap_unlock();
@@ -245,6 +251,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
if (tb == NULL) {
mmap_lock();
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
+ //AFL_QEMU_CPU_SNIPPET1;
mmap_unlock();
}
@@ -405,6 +412,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
if (tb == NULL) {
mmap_lock();
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
+ AFL_QEMU_CPU_SNIPPET1;
mmap_unlock();
/* We add the TB in the virtual pc hash table for the fast lookup */
atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
diff --git a/afl-qemu-cpu-inl.h b/afl-qemu-cpu-inl.h
new file mode 100644
index 0000000000..0347caa7e8
--- /dev/null
+++ b/afl-qemu-cpu-inl.h
@@ -0,0 +1,310 @@
+/*
+ american fuzzy lop - high-performance binary-only instrumentation
+ -----------------------------------------------------------------
+
+ Written by Andrew Griffiths <agriffiths@google.com> and
+ Michal Zalewski <lcamtuf@google.com>
+
+ Idea & design very much by Andrew Griffiths.
+
+ Copyright 2015, 2016, 2017 Google Inc. All rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at:
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ This code is a shim patched into the separately-distributed source
+ code of QEMU 2.10.0. It leverages the built-in QEMU tracing functionality
+ to implement AFL-style instrumentation and to take care of the remaining
+ parts of the AFL fork server logic.
+
+ The resulting QEMU binary is essentially a standalone instrumentation
+ tool; for an example of how to leverage it for other purposes, you can
+ have a look at afl-showmap.c.
+
+ */
+
+#include <sys/shm.h>
+
+#include "/home/alexander/builds/emu/afl-unicorn/config.h"
+//#include "config.h"
+
+/***************************
+ * VARIOUS AUXILIARY STUFF *
+ ***************************/
+
+/* A snippet patched into tb_find_slow to inform the parent process that
+ we have hit a new block that hasn't been translated yet, and to tell
+ it to translate within its own context, too (this avoids translation
+ overhead in the next forked-off copy). */
+
+#define AFL_QEMU_CPU_SNIPPET1 do { \
+ afl_request_tsl(pc, cs_base, flags); \
+ } while (0)
+
+/* This snippet kicks in when the instruction pointer is positioned at
+ _start and does the usual forkserver stuff, not very different from
+ regular instrumentation injected via afl-as.h. */
+
+#define AFL_QEMU_CPU_SNIPPET2 do { \
+ if(itb->pc == afl_entry_point) { \
+ afl_setup(); \
+ afl_forkserver(cpu); \
+ } \
+ afl_maybe_log(itb->pc); \
+ } while (0)
+
+/* We use one additional file descriptor to relay "needs translation"
+ messages between the child and the fork server. */
+
+#define TSL_FD (FORKSRV_FD - 1)
+
+/* This is equivalent to afl-as.h: */
+
+static unsigned char *afl_area_ptr;
+
+/* Exported variables populated by the code patched into elfload.c: */
+
+abi_ulong afl_entry_point, /* ELF entry point (_start) */
+ afl_start_code, /* .text start pointer */
+ afl_end_code; /* .text end pointer */
+
+/* Set in the child process in forkserver mode: */
+
+static unsigned char afl_fork_child;
+unsigned int afl_forksrv_pid;
+
+/* Instrumentation ratio: */
+
+static unsigned int afl_inst_rms = MAP_SIZE;
+
+/* Function declarations. */
+
+static void afl_setup(void);
+static void afl_forkserver(CPUState*);
+static inline void afl_maybe_log(abi_ulong);
+
+static void afl_wait_tsl(CPUState*, int);
+static void afl_request_tsl(target_ulong, target_ulong, uint64_t);
+
+/* Data structure passed around by the translate handlers: */
+
+struct afl_tsl {
+ target_ulong pc;
+ target_ulong cs_base;
+ uint64_t flags;
+};
+
+/* Some forward decls: */
+
+//TranslationBlock *tb_htable_lookup(CPUState*, target_ulong, target_ulong, uint32_t);
+//static inline TranslationBlock *tb_find(CPUState*, TranslationBlock*, int);
+
+TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
+ target_ulong cs_base, uint32_t flags,
+ uint32_t cf_mask);
+
+/*************************
+ * ACTUAL IMPLEMENTATION *
+ *************************/
+
+/* Set up SHM region and initialize other stuff. */
+
+static void afl_setup(void) {
+
+ char *id_str = getenv(SHM_ENV_VAR),
+ *inst_r = getenv("AFL_INST_RATIO");
+
+ int shm_id;
+
+ if (inst_r) {
+
+ unsigned int r;
+
+ r = atoi(inst_r);
+
+ if (r > 100) r = 100;
+ if (!r) r = 1;
+
+ afl_inst_rms = MAP_SIZE * r / 100;
+
+ }
+
+ if (id_str) {
+
+ shm_id = atoi(id_str);
+ afl_area_ptr = shmat(shm_id, NULL, 0);
+
+ if (afl_area_ptr == (void*)-1) exit(1);
+
+ /* With AFL_INST_RATIO set to a low value, we want to touch the bitmap
+ so that the parent doesn't give up on us. */
+
+ if (inst_r) afl_area_ptr[0] = 1;
+
+
+ }
+
+ if (getenv("AFL_INST_LIBS")) {
+
+ afl_start_code = 0;
+ afl_end_code = (abi_ulong)-1;
+
+ }
+
+ /* pthread_atfork() seems somewhat broken in util/rcu.c, and I'm
+ not entirely sure what is the cause. This disables that
+ behaviour, and seems to work alright? */
+
+ rcu_disable_atfork();
+
+}
+
+
+/* Fork server logic, invoked once we hit _start. */
+
+static void afl_forkserver(CPUState *cpu) {
+
+ static unsigned char tmp[4];
+
+ if (!afl_area_ptr) return;
+
+ /* Tell the parent that we're alive. If the parent doesn't want
+ to talk, assume that we're not running in forkserver mode. */
+
+ if (write(FORKSRV_FD + 1, tmp, 4) != 4) return;
+
+ afl_forksrv_pid = getpid();
+
+ /* All right, let's await orders... */
+
+ while (1) {
+
+ pid_t child_pid;
+ int status, t_fd[2];
+
+ /* Whoops, parent dead? */
+
+ if (read(FORKSRV_FD, tmp, 4) != 4) exit(2);
+
+ /* Establish a channel with child to grab translation commands. We'll
+ read from t_fd[0], child will write to TSL_FD. */
+
+ if (pipe(t_fd) || dup2(t_fd[1], TSL_FD) < 0) exit(3);
+ close(t_fd[1]);
+
+ child_pid = fork();
+ if (child_pid < 0) exit(4);
+
+ if (!child_pid) {
+
+ /* Child process. Close descriptors and run free. */
+
+ afl_fork_child = 1;
+ close(FORKSRV_FD);
+ close(FORKSRV_FD + 1);
+ close(t_fd[0]);
+ return;
+
+ }
+
+ /* Parent. */
+
+ close(TSL_FD);
+
+ if (write(FORKSRV_FD + 1, &child_pid, 4) != 4) exit(5);
+
+ /* Collect translation requests until child dies and closes the pipe. */
+
+ afl_wait_tsl(cpu, t_fd[0]);
+
+ /* Get and relay exit status to parent. */
+
+ if (waitpid(child_pid, &status, 0) < 0) exit(6);
+ if (write(FORKSRV_FD + 1, &status, 4) != 4) exit(7);
+
+ }
+
+}
+
+
+/* The equivalent of the tuple logging routine from afl-as.h. */
+
+static inline void afl_maybe_log(abi_ulong cur_loc) {
+
+ static __thread abi_ulong prev_loc;
+
+ /* Optimize for cur_loc > afl_end_code, which is the most likely case on
+ Linux systems. */
+
+ if (cur_loc > afl_end_code || cur_loc < afl_start_code || !afl_area_ptr)
+ return;
+
+ /* Looks like QEMU always maps to fixed locations, so ASAN is not a
+ concern. Phew. But instruction addresses may be aligned. Let's mangle
+ the value to get something quasi-uniform. */
+
+ cur_loc = (cur_loc >> 4) ^ (cur_loc << 8);
+ cur_loc &= MAP_SIZE - 1;
+
+ /* Implement probabilistic instrumentation by looking at scrambled block
+ address. This keeps the instrumented locations stable across runs. */
+
+ if (cur_loc >= afl_inst_rms) return;
+
+ afl_area_ptr[cur_loc ^ prev_loc]++;
+ prev_loc = cur_loc >> 1;
+
+}
+
+
+/* This code is invoked whenever QEMU decides that it doesn't have a
+ translation of a particular block and needs to compute it. When this happens,
+ we tell the parent to mirror the operation, so that the next fork() has a
+ cached copy. */
+
+static void afl_request_tsl(target_ulong pc, target_ulong cb, uint64_t flags) {
+
+ struct afl_tsl t;
+
+ if (!afl_fork_child) return;
+
+ t.pc = pc;
+ t.cs_base = cb;
+ t.flags = flags;
+
+ if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
+ return;
+
+}
+
+/* This is the other side of the same channel. Since timeouts are handled by
+ afl-fuzz simply killing the child, we can just wait until the pipe breaks. */
+
+static void afl_wait_tsl(CPUState *cpu, int fd) {
+
+ struct afl_tsl t;
+ TranslationBlock *tb;
+
+ while (1) {
+
+ /* Broken pipe means it's time to return to the fork server routine. */
+
+ if (read(fd, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
+ break;
+
+ tb = tb_htable_lookup(cpu, t.pc, t.cs_base, t.flags, 0);
+
+ if(!tb) {
+ mmap_lock();
+ tb_gen_code(cpu, t.pc, t.cs_base, t.flags, 0);
+ mmap_unlock();
+ }
+
+ }
+
+ close(fd);
+
+}
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index f84d605d3d..cb5a0501d9 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -20,6 +20,8 @@
#define ELF_OSABI ELFOSABI_SYSV
+extern abi_ulong afl_entry_point, afl_start_code, afl_end_code;
+
/* from personality.h */
/*
@@ -2368,6 +2370,8 @@ static void load_elf_image(const char *image_name, int image_fd,
fprintf(stderr, "%s: load_bias=%016x entry=%016x ehdr->e_entry=%016x [%s]\n",
__func__, (long long)load_bias, (long long)info->entry, (long long)ehdr->e_entry, image_name);
+ if (!afl_entry_point) afl_entry_point = info->entry;
+
for (i = 0; i < ehdr->e_phnum; i++) {
struct elf_phdr *eppnt = phdr + i;
if (eppnt->p_type == PT_LOAD) {
@@ -2402,9 +2406,11 @@ static void load_elf_image(const char *image_name, int image_fd,
if (elf_prot & PROT_EXEC) {
if (vaddr < info->start_code) {
info->start_code = vaddr;
+ if (!afl_start_code) afl_start_code = vaddr;
}
if (vaddr_ef > info->end_code) {
info->end_code = vaddr_ef;
+ if (!afl_end_code) afl_end_code = vaddr_ef;
}
}
if (elf_prot & PROT_WRITE) {
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 511888c9ed..40169a346c 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -112,6 +112,8 @@
#include "qemu.h"
#include "fd-trans.h"
+extern unsigned int afl_forksrv_pid;
+
#ifndef CLONE_IO
#define CLONE_IO 0x80000000 /* Clone io context */
#endif
diff --git a/teegris/afl_fuzz_qemu_mode_workaround.diff b/teegris/afl_fuzz_qemu_mode_workaround.diff
new file mode 100644
index 0000000000..c13b9a38cc
--- /dev/null
+++ b/teegris/afl_fuzz_qemu_mode_workaround.diff
@@ -0,0 +1,22 @@
+diff --git a/afl-fuzz.c b/afl-fuzz.c
+index a4b0e15..1543857 100644
+--- a/afl-fuzz.c
++++ b/afl-fuzz.c
+@@ -6934,7 +6934,7 @@ EXP_ST void check_binary(u8* fname) {
+ " QEMU or Unicorn mode (-Q or -U). This is probably not what you want -\n"
+ " this setup will be slow and offer no practical benefits.\n");
+
+- FATAL("Instrumentation found in -Q or -U mode");
++ //FATAL("Instrumentation found in -Q or -U mode");
+
+ }
+
+@@ -7991,7 +7991,7 @@ int main(int argc, char** argv) {
+
+ start_time = get_cur_time();
+
+- if (qemu_mode)
++ if (0 && qemu_mode)
+ use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind);
+ else
+ use_argv = argv + optind;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment