QEMU detects when the guest uses 'mmap' on the control channel file, and then
uses 'mprotect' to detect accesses to it, which are redirected to the
user-provided "libbackdoor.a".

Signed-off-by: Lluís Vilanova <vilan...@ac.upc.edu>
---
 Makefile.objs        |    2 +
 backdoor/qemu/user.c |  194 ++++++++++++++++++++++++++++++++++++++++++++++++++
 backdoor/qemu/user.h |   17 ++++
 bsd-user/main.c      |   25 ++++++
 bsd-user/mmap.c      |    7 ++
 configure            |    1 
 darwin-user/main.c   |   25 ++++++
 darwin-user/mmap.c   |    7 ++
 linux-user/main.c    |   30 ++++++++
 linux-user/mmap.c    |    7 ++
 10 files changed, 315 insertions(+), 0 deletions(-)
 create mode 100644 backdoor/qemu/user.c
 create mode 100644 backdoor/qemu/user.h

diff --git a/Makefile.objs b/Makefile.objs
index 2493e59..d39074d 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -397,6 +397,8 @@ $(trace-obj-y): $(GENERATED_HEADERS)
 ######################################################################
 # backdoor
 
+backdoor-nested-$(CONFIG_USER_ONLY) += user.o
+
 backdoor-obj-y += $(addprefix backdoor/qemu/, $(backdoor-nested-y))
 
 ifdef CONFIG_BACKDOOR
diff --git a/backdoor/qemu/user.c b/backdoor/qemu/user.c
new file mode 100644
index 0000000..a236ca5
--- /dev/null
+++ b/backdoor/qemu/user.c
@@ -0,0 +1,194 @@
+/*
+ * QEMU-side management of backdoor channels in user-level emulation.
+ *
+ * Copyright (C) 2011 Lluís Vilanova <vilan...@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "backdoor/qemu/user.h"
+
+#include <sys/mman.h>
+
+#include "qemu-common.h"
+#include "backdoor/qemu/qemu-backdoor.h"
+
+
+static char *data_path = NULL;
+static char *control_path = NULL;
+static int data_fd = -1;
+static int control_fd = -1;
+
+static void *data = NULL;
+static void *qemu_control_0 = NULL;
+static void *qemu_control_1 = NULL;
+
+static struct stat control_fd_stat;
+
+struct sigaction segv_next;
+static void segv_handler(int signum, siginfo_t *siginfo, void *sigctxt);
+
+
+static void init_channel(const char *base, const char *suffix, size_t size,
+                                  char ** path, int *fd, void **addr)
+{
+    *path = g_malloc(strlen(base) + strlen(suffix) + 1);
+    sprintf(*path, "%s%s", base, suffix);
+
+    *fd = open(*path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
+    if (*fd == -1) {
+        fprintf(stderr, "error: open(%s): %s\n", *path, strerror(errno));
+        abort();
+    }
+
+    off_t lres = lseek(*fd, size - 1, SEEK_SET);
+    if (lres == (off_t)-1) {
+        fprintf(stderr, "error: lseek(%s): %s\n", *path, strerror(errno));
+        abort();
+    }
+
+    char tmp;
+    ssize_t wres = write(*fd, &tmp, 1);
+    if (wres == -1) {
+        fprintf(stderr, "error: write(%s): %s\n", *path, strerror(errno));
+        abort();
+    }
+
+    if (addr) {
+        *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
+        if (*addr == MAP_FAILED) {
+            fprintf(stderr, "error: mmap(%s): %s\n", *path, strerror(errno));
+            abort();
+        }
+    }
+}
+
+void backdoor_init(const char *base, uint64_t data_size)
+{
+    if (base == NULL) {
+        return;
+    }
+
+    init_channel(base, "-data", data_size, &data_path, &data_fd, &data);
+    void *control;
+    init_channel(base, "-control", getpagesize() * 2, &control_path, 
&control_fd, &control);
+
+    /* store channel size in first 64bits; command goes into 2nd slot */
+    *(uint64_t*)control = data_size;
+
+    if (fstat(control_fd, &control_fd_stat) == -1) {
+        fprintf(stderr, "error: fstat(backdoor_control): %s\n", 
strerror(errno));
+        abort();
+    }
+
+    struct sigaction segv;
+    memset(&segv, 0, sizeof(segv));
+    segv.sa_sigaction = segv_handler;
+    segv.sa_flags = SA_SIGINFO | SA_RESTART;
+    sigemptyset(&segv.sa_mask);
+
+    if (sigaction(SIGSEGV, &segv, &segv_next) != 0) {
+        fprintf(stderr, "error: sigaction(SIGSEGV): %s\n", strerror(errno));
+        abort();
+    }
+
+    qemu_backdoor_init(data_size);
+}
+
+
+static void fini_channel(int *fd, char **path)
+{
+    if (*fd != -1) {
+        if (close(*fd) == -1) {
+            fprintf(stderr, "error: close: %s\n", strerror(errno));
+            abort();
+        }
+        if (unlink(*path) == -1) {
+            fprintf(stderr, "error: unlink(%s): %s\n", *path, strerror(errno));
+            abort();
+        }
+        *fd = -1;
+    }
+    if (*path != NULL) {
+        g_free(path);
+        *path =  NULL;
+    }
+}
+
+void backdoor_fini(void)
+{
+    static bool atexit_in = false;
+    if (atexit_in) {
+        return;
+    }
+    atexit_in = true;
+
+    if (sigaction(SIGSEGV, &segv_next, NULL) != 0) {
+        fprintf(stderr, "error: sigaction(SIGSEGV): %s\n", strerror(errno));
+        abort();
+    }
+    fini_channel(&data_fd, &data_path);
+    fini_channel(&control_fd, &control_path);
+}
+
+
+void backdoor_guest_mmap(int fd, void *qemu_addr)
+{
+    struct stat s;
+    if (fstat(fd, &s) != 0) {
+        return;
+    }
+
+    if (s.st_dev != control_fd_stat.st_dev ||
+        s.st_ino != control_fd_stat.st_ino) {
+        return;
+    }
+
+    qemu_control_0 = qemu_addr;
+    qemu_control_1 = qemu_control_0 + getpagesize();
+    if (mprotect(qemu_control_0, getpagesize(), PROT_READ) == -1) {
+        fprintf(stderr, "error: mprotect(backdoor_control): %s\n",
+                strerror(errno));
+        abort();
+    }
+}
+
+static void swap_control(void *from, void *to)
+{
+    if (mprotect(from, getpagesize(), PROT_READ | PROT_WRITE) == -1) {
+        fprintf(stderr, "error: mprotect(from): %s\n",
+                strerror(errno));
+        abort();
+    }
+    if (mprotect(to, getpagesize(), PROT_READ) == -1) {
+        fprintf(stderr, "error: mprotect(to): %s\n",
+                strerror(errno));
+        abort();
+    }
+}
+
+static void segv_handler(int signum, siginfo_t *siginfo, void *sigctxt)
+{
+    if (qemu_control_0 <= siginfo->si_addr &&
+        siginfo->si_addr < qemu_control_1) {
+
+        assert(((target_ulong)siginfo->si_addr % getpagesize()) == 
sizeof(uint64_t));
+        swap_control(qemu_control_0, qemu_control_1);
+
+    } else if (qemu_control_1 <= siginfo->si_addr &&
+               siginfo->si_addr < qemu_control_1 + getpagesize()) {
+
+        assert(((target_ulong)siginfo->si_addr % getpagesize()) == 
sizeof(uint64_t));
+        qemu_backdoor(((uint64_t*)qemu_control_0)[1], data);
+        swap_control(qemu_control_1, qemu_control_0);
+
+    } else {
+        /* proxy to next handler */
+        if (segv_next.sa_sigaction != NULL) {
+            segv_next.sa_sigaction(signum, siginfo, sigctxt);
+        } else if (segv_next.sa_handler != NULL) {
+            segv_next.sa_handler(signum);
+        }
+    }
+}
diff --git a/backdoor/qemu/user.h b/backdoor/qemu/user.h
new file mode 100644
index 0000000..1ecc8cd
--- /dev/null
+++ b/backdoor/qemu/user.h
@@ -0,0 +1,17 @@
+/*
+ * QEMU-side management of backdoor channels in user-level emulation.
+ *
+ * Copyright (C) 2011 Lluís Vilanova <vilan...@ac.upc.edu>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+
+
+void backdoor_init(const char *base, uint64_t data_size);
+/** Check if this mmap is for the control channel and act accordingly. */
+void backdoor_guest_mmap(int fd, void *qemu_addr);
+void backdoor_fini(void);
diff --git a/bsd-user/main.c b/bsd-user/main.c
index cc7d4a3..046ed87 100644
--- a/bsd-user/main.c
+++ b/bsd-user/main.c
@@ -33,6 +33,9 @@
 #include "tcg.h"
 #include "qemu-timer.h"
 #include "envlist.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
 
 #define DEBUG_LOGFILE "/tmp/qemu.log"
 
@@ -688,6 +691,11 @@ static void usage(void)
            "-B address        set guest_base address to address\n"
 #endif
            "-bsd type         select emulated BSD type FreeBSD/NetBSD/OpenBSD 
(default)\n"
+#if defined(CONFIG_BACKDOOR)
+           "-backdoor path    base path to backdoor channel\n"
+           "-backdoor-pages value\n"
+           "                  number of pages for the backdoor data channel 
(default: 1)\n"
+#endif
            "\n"
            "Debug options:\n"
            "-d options   activate log (default logfile=%s)\n"
@@ -744,6 +752,10 @@ int main(int argc, char **argv)
     char **target_environ, **wrk;
     envlist_t *envlist = NULL;
     bsd_type = target_openbsd;
+#if defined(CONFIG_BACKDOOR)
+    char *backdoor_base = NULL;
+    uint64_t backdoor_size = getpagesize();
+#endif
 
     if (argc <= 1)
         usage();
@@ -851,6 +863,16 @@ int main(int argc, char **argv)
             singlestep = 1;
         } else if (!strcmp(r, "strace")) {
             do_strace = 1;
+#if defined(CONFIG_BACKDOOR)
+        } else if (!strcmp(r, "backdoor")) {
+            if (atexit(backdoor_fini) != 0) {
+                fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+                abort();
+            }
+            backdoor_base = argv[optind++];
+        } else if (!strcmp(r, "backdoor-pages")) {
+            backdoor_size = atoi(argv[optind++]) * getpagesize();
+#endif
         } else
         {
             usage();
@@ -988,6 +1010,9 @@ int main(int argc, char **argv)
     target_set_brk(info->brk);
     syscall_init();
     signal_init();
+#if defined(CONFIG_BACKDOOR)
+    backdoor_init(backdoor_base, backdoor_size);
+#endif
 
 #if defined(CONFIG_USE_GUEST_BASE)
     /* Now that we've loaded the binary, GUEST_BASE is fixed.  Delay
diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c
index 5d6cffc..afa57df 100644
--- a/bsd-user/mmap.c
+++ b/bsd-user/mmap.c
@@ -28,6 +28,10 @@
 #include "qemu-common.h"
 #include "bsd-mman.h"
 
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
+
 //#define DEBUG_MMAP
 
 #if defined(CONFIG_USE_NPTL)
@@ -473,6 +477,9 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int 
prot,
         }
     }
  the_end1:
+#if defined(CONFIG_BACKDOOR)
+    backdoor_guest_mmap(fd, (void *)g2h(start));
+#endif
     page_set_flags(start, start + len, prot | PAGE_VALID);
  the_end:
 #ifdef DEBUG_MMAP
diff --git a/configure b/configure
index e2be271..74a6f5a 100755
--- a/configure
+++ b/configure
@@ -3216,6 +3216,7 @@ symlink $source_path/Makefile.target $target_dir/Makefile
 if test -n "$backdoor"; then
     mkdir -p $target_dir/libbackdoor
     symlink $backdoor/Makefile $target_dir/libbackdoor/Makefile
+    mkdir -p $target_dir/backdoor/qemu
 fi
 
 
diff --git a/darwin-user/main.c b/darwin-user/main.c
index 1a881a0..2b28e46 100644
--- a/darwin-user/main.c
+++ b/darwin-user/main.c
@@ -28,6 +28,9 @@
 #include <sys/mman.h>
 
 #include "qemu.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
 
 #define DEBUG_LOGFILE "/tmp/qemu.log"
 
@@ -713,6 +716,11 @@ static void usage(void)
            "-h           print this help\n"
            "-L path      set the %s library path (default='%s')\n"
            "-s size      set the stack size in bytes (default=%ld)\n"
+#if defined(CONFIG_BACKDOOR)
+           "-backdoor path    base path to backdoor channel\n"
+           "-backdoor-pages value\n"
+           "                  number of pages for the backdoor data channel 
(default: 1)\n"
+#endif
            "\n"
            "debug options:\n"
            "-d options   activate log (logfile='%s')\n"
@@ -747,6 +755,10 @@ int main(int argc, char **argv)
     short use_gdbstub = 0;
     const char *r;
     const char *cpu_model;
+#if defined(CONFIG_BACKDOOR)
+    char *backdoor_base = NULL;
+    uint64_t backdoor_size = getpagesize();
+#endif
 
     if (argc <= 1)
         usage();
@@ -804,6 +816,16 @@ int main(int argc, char **argv)
             }
         } else if (!strcmp(r, "singlestep")) {
             singlestep = 1;
+#if defined(CONFIG_BACKDOOR)
+        } else if (!strcmp(r, "backdoor")) {
+            if (atexit(backdoor_fini) != 0) {
+                fprintf(stderr, "error: atexit: %s\n", strerror(errno));
+                abort();
+            }
+            backdoor_base = argv[optind++];
+        } else if (!strcmp(r, "backdoor-pages")) {
+            backdoor_size = atoi(argv[optind++]) * getpagesize();
+#endif
         } else
         {
             usage();
@@ -870,6 +892,9 @@ int main(int argc, char **argv)
 
     syscall_init();
     signal_init();
+#if defined(CONFIG_BACKDOOR)
+    backdoor_init(backdoor_base, backdoor_size);
+#endif
     global_env = env;
 
     /* build Task State */
diff --git a/darwin-user/mmap.c b/darwin-user/mmap.c
index d840b28..047c5bf 100644
--- a/darwin-user/mmap.c
+++ b/darwin-user/mmap.c
@@ -26,6 +26,10 @@
 
 #include "qemu.h"
 
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
+
 //#define DEBUG_MMAP
 
 /* NOTE: all the constants are the HOST ones */
@@ -308,6 +312,9 @@ long target_mmap(unsigned long start, unsigned long len, 
int prot,
             return ret;
     }
  the_end1:
+#if defined(CONFIG_BACKDOOR)
+    backdoor_guest_mmap(fd, (void *)g2h(start));
+#endif
     page_set_flags(start, start + len, prot | PAGE_VALID);
  the_end:
 #ifdef DEBUG_MMAP
diff --git a/linux-user/main.c b/linux-user/main.c
index 186358b..4d5ef97 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -33,6 +33,9 @@
 #include "tcg.h"
 #include "qemu-timer.h"
 #include "envlist.h"
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
 
 #define DEBUG_LOGFILE "/tmp/qemu.log"
 
@@ -50,6 +53,10 @@ unsigned long guest_base;
 int have_guest_base;
 unsigned long reserved_va;
 #endif
+#if defined(CONFIG_BACKDOOR)
+const char *backdoor_base = NULL;
+uint64_t backdoor_size;
+#endif
 
 static void usage(void);
 
@@ -3080,6 +3087,18 @@ static void handle_arg_strace(const char *arg)
     do_strace = 1;
 }
 
+#if defined(CONFIG_BACKDOOR)
+static void handle_arg_backdoor(const char *arg)
+{
+    backdoor_base = arg;
+}
+
+static void handle_arg_backdoor_pages(const char *arg)
+{
+    backdoor_size = atoi(arg) * getpagesize();
+}
+#endif
+
 static void handle_arg_version(const char *arg)
 {
     printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION
@@ -3120,6 +3139,12 @@ struct qemu_argument arg_table[] = {
     {"R",          "QEMU_RESERVED_VA", true,  handle_arg_reserved_va,
      "size",       "reserve 'size' bytes for guest virtual address space"},
 #endif
+#if defined(CONFIG_BACKDOOR)
+    {"backdoor",   "QEMU_BACKDOOR",    true,  handle_arg_backdoor,
+     "path",       "base path to backdoor channel\n"},
+    {"backdoor-pages", "QEMU_BACKDOOR_PAGES", true,  handle_arg_backdoor_pages,
+     "num",        "number of pages for the backdoor data channel (default: 
1)\n"},
+#endif
     {"d",          "QEMU_LOG",         true,  handle_arg_log,
      "options",    "activate log"},
     {"p",          "QEMU_PAGESIZE",    true,  handle_arg_pagesize,
@@ -3279,6 +3304,8 @@ int main(int argc, char **argv, char **envp)
     if (argc <= 1)
         usage();
 
+    backdoor_size = getpagesize();
+
     qemu_cache_utils_init(envp);
 
     if ((envlist = envlist_create()) == NULL) {
@@ -3511,6 +3538,9 @@ int main(int argc, char **argv, char **envp)
     target_set_brk(info->brk);
     syscall_init();
     signal_init();
+#if defined(CONFIG_BACKDOOR)
+    backdoor_init(backdoor_base, backdoor_size);
+#endif
 
 #if defined(CONFIG_USE_GUEST_BASE)
     /* Now that we've loaded the binary, GUEST_BASE is fixed.  Delay
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 994c02b..2232363 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -31,6 +31,10 @@
 #include "qemu.h"
 #include "qemu-common.h"
 
+#if defined(CONFIG_BACKDOOR)
+#include "backdoor/qemu/user.h"
+#endif
+
 //#define DEBUG_MMAP
 
 #if defined(CONFIG_USE_NPTL)
@@ -553,6 +557,9 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int 
prot,
         }
     }
  the_end1:
+#if defined(CONFIG_BACKDOOR)
+    backdoor_guest_mmap(fd, (void *)g2h(start));
+#endif
     page_set_flags(start, start + len, prot | PAGE_VALID);
  the_end:
 #ifdef DEBUG_MMAP


Reply via email to