about summary refs log tree commit diff
path: root/unicorn_mode/patches
diff options
context:
space:
mode:
Diffstat (limited to 'unicorn_mode/patches')
-rw-r--r--unicorn_mode/patches/afl-unicorn-cpu-inl.h290
-rw-r--r--unicorn_mode/patches/patches.diff107
2 files changed, 397 insertions, 0 deletions
diff --git a/unicorn_mode/patches/afl-unicorn-cpu-inl.h b/unicorn_mode/patches/afl-unicorn-cpu-inl.h
new file mode 100644
index 00000000..892c3f72
--- /dev/null
+++ b/unicorn_mode/patches/afl-unicorn-cpu-inl.h
@@ -0,0 +1,290 @@
+/*
+   american fuzzy lop - high-performance binary-only instrumentation
+   -----------------------------------------------------------------
+
+   Written by Andrew Griffiths <agriffiths@google.com> and
+              Michal Zalewski <lcamtuf@google.com>
+
+   TCG instrumentation and block chaining support by Andrea Biondo
+                                      <andrea.biondo965@gmail.com>
+   Adapted for afl-unicorn by Dominik Maier <mail@dmnk.co>
+
+   Idea & design very much by Andrew Griffiths.
+
+   Copyright 2015, 2016 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 Unicorn 1.0.1. 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 <sys/types.h>
+#include <sys/wait.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_UNICORN_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_UNICORN_CPU_SNIPPET2 do { \
+    if(unlikely(afl_first_instr == 0)) { \
+      afl_setup(); \
+      afl_forkserver(env); \
+      afl_first_instr = 1; \
+    } \
+    afl_maybe_log(tb->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;
+
+/* Set in the child process in forkserver mode: */
+
+static unsigned char afl_fork_child;
+static 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(CPUArchState*);
+static inline void afl_maybe_log(unsigned long);
+
+static void afl_wait_tsl(CPUArchState*, int);
+static void afl_request_tsl(target_ulong, target_ulong, uint64_t);
+
+static TranslationBlock *tb_find_slow(CPUArchState*, 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;
+};
+
+/*************************
+ * 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;
+  }
+}
+
+/* Fork server logic, invoked once we hit first emulated instruction. */
+
+static void afl_forkserver(CPUArchState *env) {
+
+  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(env, 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(unsigned long cur_loc) {
+
+  static __thread unsigned long prev_loc;
+
+  // DEBUG
+  //printf("IN AFL_MAYBE_LOG 0x%lx\n", cur_loc);
+
+  // MODIFIED FOR UNICORN MODE -> We want to log all addresses,
+  // so the checks for 'start < addr < end' are removed
+  if(!afl_area_ptr)
+    return;
+
+  // DEBUG
+  //printf("afl_area_ptr = %p\n", afl_area_ptr);
+
+  /* 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. */
+
+  // DEBUG
+  //printf("afl_inst_rms = 0x%lx\n", afl_inst_rms);
+
+  if (cur_loc >= afl_inst_rms) return;
+
+  // DEBUG
+  //printf("cur_loc = 0x%lx\n", cur_loc);  
+
+  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(CPUArchState *env, int fd) {
+
+  struct afl_tsl t;
+
+  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_find_slow(env, t.pc, t.cs_base, t.flags);
+  }
+
+  close(fd);
+}
+
diff --git a/unicorn_mode/patches/patches.diff b/unicorn_mode/patches/patches.diff
new file mode 100644
index 00000000..71a1e2f3
--- /dev/null
+++ b/unicorn_mode/patches/patches.diff
@@ -0,0 +1,107 @@
+diff --git a/Makefile b/Makefile
+index 7d73782..fb3ccfd 100644
+--- a/Makefile
++++ b/Makefile
+@@ -88,6 +88,10 @@ AR = llvm-ar
+ LDFLAGS := -fsanitize=address ${LDFLAGS}
+ endif
+ 
++ifeq ($(UNICORN_AFL),yes)
++UNICORN_CFLAGS += -DUNICORN_AFL
++endif
++
+ ifeq ($(CROSS),)
+ CC ?= cc
+ AR ?= ar
+diff --git a/config.mk b/config.mk
+index c3621fb..c7b4f7e 100644
+--- a/config.mk
++++ b/config.mk
+@@ -8,7 +8,7 @@
+ # Compile with debug info when you want to debug code.
+ # Change this to 'no' for release edition.
+ 
+-UNICORN_DEBUG ?= yes
++UNICORN_DEBUG ?= no
+ 
+ ################################################################################
+ # Specify which archs you want to compile in. By default, we build all archs.
+@@ -28,3 +28,9 @@ UNICORN_STATIC ?= yes
+ # a shared library.
+ 
+ UNICORN_SHARED ?= yes
++
++
++################################################################################
++# Changing 'UNICORN_AFLL = yes' to 'UNICORN_AFL = no' disables AFL instrumentation
++
++UNICORN_AFL ?= yes
+diff --git a/qemu/cpu-exec.c b/qemu/cpu-exec.c
+index 7755adf..8114b70 100644
+--- a/qemu/cpu-exec.c
++++ b/qemu/cpu-exec.c
+@@ -24,6 +24,11 @@
+ 
+ #include "uc_priv.h"
+ 
++#if defined(UNICORN_AFL)
++#include "../afl-unicorn-cpu-inl.h"
++static int afl_first_instr = 0;
++#endif 
++
+ static tcg_target_ulong cpu_tb_exec(CPUState *cpu, uint8_t *tb_ptr);
+ static TranslationBlock *tb_find_slow(CPUArchState *env, target_ulong pc,
+         target_ulong cs_base, uint64_t flags);
+@@ -231,6 +236,10 @@ int cpu_exec(struct uc_struct *uc, CPUArchState *env)   // qq
+                             next_tb & TB_EXIT_MASK, tb);
+                 }
+ 
++#if defined(UNICORN_AFL)
++                AFL_UNICORN_CPU_SNIPPET2;
++#endif
++
+                 /* cpu_interrupt might be called while translating the
+                    TB, but before it is linked into a potentially
+                    infinite loop and becomes env->current_tb. Avoid
+@@ -369,6 +378,11 @@ static TranslationBlock *tb_find_slow(CPUArchState *env, target_ulong pc,
+ not_found:
+     /* if no translated code available, then translate it now */
+     tb = tb_gen_code(cpu, pc, cs_base, (int)flags, 0);   // qq
++    
++#if defined(UNICORN_AFL)
++    /* There seems to be no chaining in unicorn ever? :( */
++    AFL_UNICORN_CPU_SNIPPET1;
++#endif
+ 
+ found:
+     /* Move the last found TB to the head of the list */
+diff --git a/qemu/translate-all.c b/qemu/translate-all.c
+index 1a96c34..7ef4878 100644
+--- a/qemu/translate-all.c
++++ b/qemu/translate-all.c
+@@ -403,11 +403,25 @@ static PageDesc *page_find_alloc(struct uc_struct *uc, tb_page_addr_t index, int
+ 
+ #if defined(CONFIG_USER_ONLY)
+     /* We can't use g_malloc because it may recurse into a locked mutex. */
++#if defined(UNICORN_AFL)
++    /* This was added by unicorn-afl to bail out semi-gracefully if out of memory. */
++# define ALLOC(P, SIZE)                                 \
++    do {                                                \
++        void* _tmp = mmap(NULL, SIZE, PROT_READ | PROT_WRITE,    \
++                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);   \
++        if (_tmp == (void*)-1) { \
++            qemu_log(">>> Out of memory for stack, bailing out. <<<\n"); \
++            exit(1); \
++        } \
++        (P) = _tmp; \
++    } while (0)
++#else /* !UNICORN_AFL */
+ # define ALLOC(P, SIZE)                                 \
+     do {                                                \
+         P = mmap(NULL, SIZE, PROT_READ | PROT_WRITE,    \
+                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);   \
+     } while (0)
++#endif /* UNICORN_AFL */
+ #else
+ # define ALLOC(P, SIZE) \
+     do { P = g_malloc0(SIZE); } while (0)