]> TLD Linux GIT Repositories - packages/qemu.git/blobdiff - qemu-user-execve.patch
- updated to 2.8.0, partial PLD merge
[packages/qemu.git] / qemu-user-execve.patch
diff --git a/qemu-user-execve.patch b/qemu-user-execve.patch
new file mode 100644 (file)
index 0000000..78c0e57
--- /dev/null
@@ -0,0 +1,261 @@
+https://resin.io/blog/building-arm-containers-on-any-x86-machine-even-dockerhub/
+https://github.com/resin-io/qemu/commit/782e5bb77014ff136f7bb6133a911e5f53e914a7
+
+https://github.com/resin-io/qemu/commit/782e5bb77014ff136f7bb6133a911e5f53e914a7#commitcomment-17193923
+It has gone through review[1][2][3] and I'm waiting for the maintainer of the linux-user subsystem to accept it in his tree.
+
+[1] https://patchwork.ozlabs.org/patch/569452/
+[2] https://patchwork.ozlabs.org/patch/573877/
+[3] https://patchwork.ozlabs.org/patch/582756/
+
+From patchwork Mon Feb 15 05:51:47 2016
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Subject: [v3] linux-user: add option to intercept execve() syscalls
+From: Petros Angelatos <petrosagg@resin.io>
+X-Patchwork-Id: 582756
+Message-Id: <1455515507-26877-1-git-send-email-petrosagg@resin.io>
+To: qemu-devel@nongnu.org
+Cc: lucas.kaldstrom@hotmail.co.uk, peter.maydell@linaro.org,
+ riku.voipio@iki.fi, 
+ laurent@vivier.eu, Petros Angelatos <petrosagg@resin.io>
+Date: Sun, 14 Feb 2016 21:51:47 -0800
+
+In order for one to use QEMU user mode emulation under a chroot, it is
+required to use binfmt_misc. This can be avoided by QEMU never doing a
+raw execve() to the host system.
+
+Introduce a new option, -execve, that uses the current QEMU interpreter
+to intercept execve().
+
+qemu_execve() will prepend the interpreter path , similar to what
+binfmt_misc would do, and then pass the modified execve() to the host.
+
+It is necessary to parse hashbang scripts in that function otherwise
+the kernel will try to run the interpreter of a script without QEMU and
+get an invalid exec format error.
+
+Signed-off-by: Petros Angelatos <petrosagg@resin.io>
+Tested-by: Laurent Vivier <laurent@vivier.eu>
+Reviewed-by: Laurent Vivier <laurent@vivier.eu>
+---
+v3 changes:
+       - rebase the patchset against current code
+
+--- qemu-2.7.0/linux-user/main.c~      2016-09-26 12:07:20.000000000 +0300
++++ qemu-2.7.0/linux-user/main.c       2016-09-26 12:09:24.258470304 +0300
+@@ -18,6 +18,7 @@
+  */
+ #include "qemu/osdep.h"
+ #include "qemu-version.h"
++#include <sys/auxv.h>
+ #include <sys/syscall.h>
+ #include <sys/resource.h>
+@@ -75,6 +76,7 @@ static void usage(int exitcode);
+ static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
+ const char *qemu_uname_release;
++const char *qemu_execve_path;
+ /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
+    we allocate a bigger stack. Need a better solution, for example
+@@ -3824,6 +3826,38 @@ static void handle_arg_guest_base(const char *arg)
+     have_guest_base = 1;
+ }
++static void handle_arg_execve(const char *arg)
++{
++    const char *execfn;
++    char buf[PATH_MAX];
++    char *ret;
++    int len;
++
++    /* try getauxval() */
++    execfn = (const char *) getauxval(AT_EXECFN);
++
++    if (execfn != 0) {
++        ret = realpath(execfn, buf);
++
++        if (ret != NULL) {
++            qemu_execve_path = strdup(buf);
++            return;
++        }
++    }
++
++    /* try /proc/self/exe */
++    len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
++
++    if (len != -1) {
++        buf[len] = '\0';
++        qemu_execve_path = strdup(buf);
++        return;
++    }
++
++    fprintf(stderr, "qemu_execve: unable to determine intepreter's path\n");
++    exit(EXIT_FAILURE);
++}
++
+ static void handle_arg_reserved_va(const char *arg)
+ {
+     char *p;
+@@ -3909,6 +3943,8 @@ static const struct qemu_argument arg_table[] = {
+      "uname",      "set qemu uname release string to 'uname'"},
+     {"B",          "QEMU_GUEST_BASE",  true,  handle_arg_guest_base,
+      "address",    "set guest_base address to 'address'"},
++    {"execve",     "QEMU_EXECVE",      false, handle_arg_execve,
++     "",           "use this interpreter when a process calls execve()"},
+     {"R",          "QEMU_RESERVED_VA", true,  handle_arg_reserved_va,
+      "size",       "reserve 'size' bytes for guest virtual address space"},
+     {"d",          "QEMU_LOG",         true,  handle_arg_log,
+diff --git a/linux-user/qemu.h b/linux-user/qemu.h
+index bd90cc3..0d9b058 100644
+--- a/linux-user/qemu.h
++++ b/linux-user/qemu.h
+@@ -140,6 +140,7 @@ void init_task_state(TaskState *ts);
+ void task_settid(TaskState *);
+ void stop_all_tasks(void);
+ extern const char *qemu_uname_release;
++extern const char *qemu_execve_path;
+ extern unsigned long mmap_min_addr;
+ /* ??? See if we can avoid exposing so much of the loader internals.  */
+--- qemu-2.7.0/linux-user/syscall.c~   2016-09-26 12:10:36.000000000 +0300
++++ qemu-2.7.0/linux-user/syscall.c    2016-09-26 12:13:54.312490312 +0300
+@@ -99,6 +99,7 @@
+ #include <linux/reboot.h>
+ #include <linux/route.h>
+ #include <linux/filter.h>
++#include <linux/binfmts.h>
+ #include <linux/blkpg.h>
+ #include <netpacket/packet.h>
+ #include <linux/netlink.h>
+@@ -5842,6 +5843,118 @@ static target_timer_t get_timer_id(abi_long arg)
+     return timerid;
+ }
++/* qemu_execve() Must return target values and target errnos. */
++static abi_long qemu_execve(char *filename, char *argv[],
++                  char *envp[])
++{
++    char *i_arg = NULL, *i_name = NULL;
++    char **new_argp;
++    int argc, fd, ret, i, offset = 3;
++    char *cp;
++    char buf[BINPRM_BUF_SIZE];
++
++    /* normal execve case */
++    if (qemu_execve_path == NULL || *qemu_execve_path == 0) {
++        return get_errno(execve(filename, argv, envp));
++    }
++
++    for (argc = 0; argv[argc] != NULL; argc++) {
++        /* nothing */ ;
++    }
++
++    fd = open(filename, O_RDONLY);
++    if (fd == -1) {
++        return get_errno(fd);
++    }
++
++    ret = read(fd, buf, BINPRM_BUF_SIZE);
++    if (ret == -1) {
++        close(fd);
++        return get_errno(ret);
++    }
++
++    /* if we have less than 2 bytes, we can guess it is not executable */
++    if (ret < 2) {
++        close(fd);
++        return -host_to_target_errno(ENOEXEC);
++    }
++
++    close(fd);
++
++    /* adapted from the kernel
++     * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c
++     */
++    if ((buf[0] == '#') && (buf[1] == '!')) {
++        /*
++         * This section does the #! interpretation.
++         * Sorta complicated, but hopefully it will work.  -TYT
++         */
++
++        buf[BINPRM_BUF_SIZE - 1] = '\0';
++        cp = strchr(buf, '\n');
++        if (cp == NULL) {
++            cp = buf + BINPRM_BUF_SIZE - 1;
++        }
++        *cp = '\0';
++        while (cp > buf) {
++            cp--;
++            if ((*cp == ' ') || (*cp == '\t')) {
++                *cp = '\0';
++            } else {
++                break;
++            }
++        }
++        for (cp = buf + 2; (*cp == ' ') || (*cp == '\t'); cp++) {
++            /* nothing */ ;
++        }
++        if (*cp == '\0') {
++            return -ENOEXEC; /* No interpreter name found */
++        }
++        i_name = cp;
++        i_arg = NULL;
++        for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
++            /* nothing */ ;
++        }
++        while ((*cp == ' ') || (*cp == '\t')) {
++            *cp++ = '\0';
++        }
++        if (*cp) {
++            i_arg = cp;
++        }
++
++        if (i_arg) {
++            offset = 5;
++        } else {
++            offset = 4;
++        }
++    }
++
++    new_argp = alloca((argc + offset + 1) * sizeof(void *));
++
++    /* Copy the original arguments with offset */
++    for (i = 0; i < argc; i++) {
++        new_argp[i + offset] = argv[i];
++    }
++
++    new_argp[0] = strdup(qemu_execve_path);
++    new_argp[1] = strdup("-0");
++    new_argp[offset] = filename;
++    new_argp[argc + offset] = NULL;
++
++    if (i_name) {
++        new_argp[2] = i_name;
++        new_argp[3] = i_name;
++
++        if (i_arg) {
++            new_argp[4] = i_arg;
++        }
++    } else {
++        new_argp[2] = argv[0];
++    }
++
++    return get_errno(safe_execve(qemu_execve_path, new_argp, envp));
++}
++
+ /* do_syscall() should always have a single exit point at the end so
+    that actions, such as logging of syscall results, can be performed.
+    All errnos that do_syscall() returns must be -TARGET_<errcode>. */
+@@ -7703,7 +7703,7 @@
+              * before the execve completes and makes it the other
+              * program's problem.
+              */
+-            ret = get_errno(safe_execve(p, argp, envp));
++            ret = qemu_execve(p, argp, envp);
+             unlock_user(p, arg1, 0);
+             goto execve_end;