about summary refs log tree commit diff
path: root/qemu_mode/patches/afl-qemu-cpu-inl.h
diff options
context:
space:
mode:
Diffstat (limited to 'qemu_mode/patches/afl-qemu-cpu-inl.h')
-rw-r--r--qemu_mode/patches/afl-qemu-cpu-inl.h356
1 files changed, 356 insertions, 0 deletions
diff --git a/qemu_mode/patches/afl-qemu-cpu-inl.h b/qemu_mode/patches/afl-qemu-cpu-inl.h
new file mode 100644
index 00000000..f7a32c4c
--- /dev/null
+++ b/qemu_mode/patches/afl-qemu-cpu-inl.h
@@ -0,0 +1,356 @@
+/*
+   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.
+
+   TCG instrumentation and block chaining support by Andrea Biondo
+                                      <andrea.biondo965@gmail.com>
+
+   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 "../../config.h"
+
+/***************************
+ * VARIOUS AUXILIARY STUFF *
+ ***************************/
+
+/* 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); \
+    } \
+  } 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 dummy[65536];
+unsigned char *afl_area_ptr = dummy;
+
+/* 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: */
+
+unsigned int afl_inst_rms = MAP_SIZE; /* Exported for afl_gen_trace */
+
+/* Function declarations. */
+
+static void afl_setup(void);
+static void afl_forkserver(CPUState*);
+
+static void afl_wait_tsl(CPUState*, int);
+static void afl_request_tsl(target_ulong, target_ulong, uint32_t, TranslationBlock*, int);
+
+/* Data structures passed around by the translate handlers: */
+
+struct afl_tb {
+  target_ulong pc;
+  target_ulong cs_base;
+  uint32_t flags;
+};
+
+struct afl_tsl {
+  struct afl_tb tb;
+  char is_chain;
+};
+
+struct afl_chain {
+  struct afl_tb last_tb;
+  int tb_exit;
+};
+
+/* Some forward decls: */
+
+TranslationBlock *tb_htable_lookup(CPUState*, target_ulong, target_ulong, uint32_t);
+static inline TranslationBlock *tb_find(CPUState*, TranslationBlock*, int);
+
+/*************************
+ * 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 int forkserver_installed = 0;
+static void afl_forkserver(CPUState *cpu) {
+  if (forkserver_installed == 1)
+    return;
+  forkserver_installed = 1;
+
+  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);
+
+  }
+
+}
+
+
+#if 0
+/* 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;
+
+}
+#endif
+
+/* 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. */
+
+#if 0
+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;
+
+}
+#else
+static void afl_request_tsl(target_ulong pc, target_ulong cb, uint32_t flags,
+                            TranslationBlock *last_tb, int tb_exit) {
+  struct afl_tsl t;
+  struct afl_chain c;
+ 
+  if (!afl_fork_child) return;
+ 
+  t.tb.pc      = pc;
+  t.tb.cs_base = cb;
+  t.tb.flags   = flags;
+  t.is_chain   = (last_tb != NULL);
+ 
+  if (write(TSL_FD, &t, sizeof(struct afl_tsl)) != sizeof(struct afl_tsl))
+    return;
+ 
+  if (t.is_chain) {
+    c.last_tb.pc      = last_tb->pc;
+    c.last_tb.cs_base = last_tb->cs_base;
+    c.last_tb.flags   = last_tb->flags;
+    c.tb_exit         = tb_exit;
+
+    if (write(TSL_FD, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
+      return;
+  }
+ }
+#endif
+
+/* 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;
+  struct afl_chain c;
+  TranslationBlock *tb, *last_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.tb.pc, t.tb.cs_base, t.tb.flags);
+
+    if(!tb) {
+      mmap_lock();
+      tb_lock();
+      tb = tb_gen_code(cpu, t.tb.pc, t.tb.cs_base, t.tb.flags, 0);
+      mmap_unlock();
+      tb_unlock();
+    }
+
+    if (t.is_chain) {
+      if (read(fd, &c, sizeof(struct afl_chain)) != sizeof(struct afl_chain))
+        break;
+
+      last_tb = tb_htable_lookup(cpu, c.last_tb.pc, c.last_tb.cs_base,
+                                 c.last_tb.flags);
+      if (last_tb) {
+        tb_lock();
+        if (!tb->invalid) {
+          tb_add_jump(last_tb, c.tb_exit, tb);
+        }
+        tb_unlock();
+      }
+    }
+
+  }
+
+  close(fd);
+
+}