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