From patchwork Mon Dec 5 22:22:56 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Llu=C3=ADs_Vilanova?= X-Patchwork-Id: 129453 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id A61451007D4 for ; Tue, 6 Dec 2011 09:23:32 +1100 (EST) Received: from localhost ([::1]:36989 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RXgwP-0006P2-KF for incoming@patchwork.ozlabs.org; Mon, 05 Dec 2011 17:23:17 -0500 Received: from eggs.gnu.org ([140.186.70.92]:36556) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RXgwH-0006L5-Eu for qemu-devel@nongnu.org; Mon, 05 Dec 2011 17:23:11 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1RXgwF-00011Q-59 for qemu-devel@nongnu.org; Mon, 05 Dec 2011 17:23:09 -0500 Received: from gw.ac.upc.edu ([147.83.30.3]:47478) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1RXgwE-00011M-Bu for qemu-devel@nongnu.org; Mon, 05 Dec 2011 17:23:07 -0500 Received: from localhost (unknown [84.88.53.92]) by gw.ac.upc.edu (Postfix) with ESMTP id 0400B6B01CA; Mon, 5 Dec 2011 23:23:05 +0100 (CET) To: qemu-devel@nongnu.org From: =?utf-8?b?TGx1w61z?= Vilanova Date: Mon, 05 Dec 2011 23:22:56 +0100 Message-ID: <20111205222256.31271.40087.stgit@ginnungagap.bsc.es> In-Reply-To: <20111205222208.31271.65662.stgit@ginnungagap.bsc.es> References: <20111205222208.31271.65662.stgit@ginnungagap.bsc.es> User-Agent: StGit/0.15 MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 147.83.30.3 Cc: Blue Swirl , Zhi Yong Wu Subject: [Qemu-devel] [PATCH v2 3/5] backdoor: [*-user] Add QEMU-side proxy to "libbackdoor.a" X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org 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 --- Makefile.objs | 2 backdoor/qemu/user.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++ backdoor/qemu/user.h | 30 +++++++ bsd-user/main.c | 26 ++++++ bsd-user/mmap.c | 7 ++ bsd-user/syscall.c | 13 +++ configure | 1 darwin-user/main.c | 26 ++++++ darwin-user/mmap.c | 7 ++ linux-user/main.c | 32 ++++++++ linux-user/mmap.c | 7 ++ linux-user/syscall.c | 10 ++ 12 files changed, 363 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 df943e9..9784441 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -400,6 +400,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..6e178cc --- /dev/null +++ b/backdoor/qemu/user.c @@ -0,0 +1,202 @@ +/* + * QEMU-side management of backdoor channels in user-level emulation. + * + * Copyright (C) 2011 Lluís Vilanova + * + * 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 +#include + +#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 (target endianess); command goes into + * 2nd slot + */ + *(uint64_t*)control = tswap64(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; + } + + /* it's an mmap of the control channel; split it in two and mprotect it to + * detect writes (cmd is written once on each part) + */ + 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) { + + /* 1st fault (guest will write cmd) */ + 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()) { + + /* 2nd fault (invoke) */ + 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..04f9aea --- /dev/null +++ b/backdoor/qemu/user.h @@ -0,0 +1,30 @@ +/* + * QEMU-side management of backdoor channels in user-level emulation. + * + * Copyright (C) 2011 Lluís Vilanova + * + * 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 +#include + + +/** + * Initialize the backing files for the backdoor channel. + * + * @param base Base path for the backdoor channel files. + * @param data_size Length in bytes for the data channel. + */ +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); + +/** + * Remove the backing files for the backdoor channel. + */ +void backdoor_fini(void); diff --git a/bsd-user/main.c b/bsd-user/main.c index cc7d4a3..0c6c42c 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -34,6 +34,10 @@ #include "qemu-timer.h" #include "envlist.h" +#if defined(CONFIG_BACKDOOR) +#include "backdoor/qemu/user.h" +#endif + #define DEBUG_LOGFILE "/tmp/qemu.log" int singlestep; @@ -688,6 +692,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 +753,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_pages = 1; +#endif if (argc <= 1) usage(); @@ -851,6 +864,12 @@ 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")) { + backdoor_base = argv[optind++]; + } else if (!strcmp(r, "backdoor-pages")) { + backdoor_pages = atoi(argv[optind++]); +#endif } else { usage(); @@ -988,6 +1007,13 @@ int main(int argc, char **argv) target_set_brk(info->brk); syscall_init(); signal_init(); +#if defined(CONFIG_BACKDOOR) + if (atexit(backdoor_fini) != 0) { + fprintf(stderr, "error: atexit: %s\n", strerror(errno)); + abort(); + } + backdoor_init(backdoor_base, backdoor_pages * getpagesize()); +#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/bsd-user/syscall.c b/bsd-user/syscall.c index 18b43f1..fa621ea 100644 --- a/bsd-user/syscall.c +++ b/bsd-user/syscall.c @@ -36,6 +36,10 @@ #include "qemu.h" #include "qemu-common.h" +#if defined(CONFIG_BACKDOOR) +#include "backdoor/qemu/user.h" +#endif + //#define DEBUG static abi_ulong target_brk; @@ -334,6 +338,9 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, _mcleanup(); #endif gdb_exit(cpu_env, arg1); +#if defined(CONFIG_BACKDOOR) + backdoor_fini(); +#endif /* XXX: should free thread stack and CPU env */ _exit(arg1); ret = 0; /* avoid warning */ @@ -429,6 +436,9 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, _mcleanup(); #endif gdb_exit(cpu_env, arg1); +#if defined(CONFIG_BACKDOOR) + backdoor_fini(); +#endif /* XXX: should free thread stack and CPU env */ _exit(arg1); ret = 0; /* avoid warning */ @@ -501,6 +511,9 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, _mcleanup(); #endif gdb_exit(cpu_env, arg1); +#if defined(CONFIG_BACKDOOR) + backdoor_fini(); +#endif /* XXX: should free thread stack and CPU env */ _exit(arg1); ret = 0; /* avoid warning */ diff --git a/configure b/configure index f362ef6..8af8313 100755 --- a/configure +++ b/configure @@ -3373,6 +3373,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 c0f14f8..aa57140 100644 --- a/darwin-user/main.c +++ b/darwin-user/main.c @@ -28,6 +28,9 @@ #include #include "qemu.h" +#if defined(CONFIG_BACKDOOR) +#include "backdoor/qemu/user.h" +#endif #define DEBUG_LOGFILE "/tmp/qemu.log" @@ -713,6 +716,12 @@ 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\n" + " 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" @@ -745,6 +754,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_pages = 1; +#endif if (argc <= 1) usage(); @@ -802,6 +815,12 @@ int main(int argc, char **argv) } } else if (!strcmp(r, "singlestep")) { singlestep = 1; +#if defined(CONFIG_BACKDOOR) + } else if (!strcmp(r, "backdoor")) { + backdoor_base = argv[optind++]; + } else if (!strcmp(r, "backdoor-pages")) { + backdoor_pages = atoi(argv[optind++]); +#endif } else { usage(); @@ -868,6 +887,13 @@ int main(int argc, char **argv) syscall_init(); signal_init(); +#if defined(CONFIG_BACKDOOR) + if (atexit(backdoor_fini) != 0) { + fprintf(stderr, "error: atexit: %s\n", strerror(errno)); + abort(); + } + backdoor_init(backdoor_base, backdoor_pages * getpagesize()); +#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 d1bbc57..0c1db30 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_pages = 1; +#endif static void usage(void); @@ -3082,6 +3089,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_pages = atoi(arg); +} +#endif + static void handle_arg_version(const char *arg) { printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION @@ -3123,6 +3142,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"}, + {"backdoor-pages", "QEMU_BACKDOOR_PAGES", true, handle_arg_backdoor_pages, + "num", "number of pages for the backdoor data channel (default: 1)"}, +#endif {"d", "QEMU_LOG", true, handle_arg_log, "options", "activate log"}, {"p", "QEMU_PAGESIZE", true, handle_arg_pagesize, @@ -3510,6 +3535,13 @@ int main(int argc, char **argv, char **envp) target_set_brk(info->brk); syscall_init(); signal_init(); +#if defined(CONFIG_BACKDOOR) + if (atexit(backdoor_fini)) { + fprintf(stderr, "error: atexit: %s\n", strerror(errno)); + abort(); + } + backdoor_init(backdoor_base, backdoor_pages * getpagesize()); +#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 diff --git a/linux-user/syscall.c b/linux-user/syscall.c index f227097..d2dc11e 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -100,6 +100,10 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include "qemu.h" +#if defined(CONFIG_BACKDOOR) +#include "backdoor/qemu/user.h" +#endif + #if defined(CONFIG_USE_NPTL) #define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \ CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID) @@ -4663,6 +4667,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, _mcleanup(); #endif gdb_exit(cpu_env, arg1); +#if defined(CONFIG_BACKDOOR) + backdoor_fini(); +#endif _exit(arg1); ret = 0; /* avoid warning */ break; @@ -6397,6 +6404,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, _mcleanup(); #endif gdb_exit(cpu_env, arg1); +#if defined(CONFIG_BACKDOOR) + backdoor_fini(); +#endif ret = get_errno(exit_group(arg1)); break; #endif