X-Git-Url: https://git.tld-linux.org/?a=blobdiff_plain;f=qemu-user-execve.patch;fp=qemu-user-execve.patch;h=78c0e57e89af77d79623055e370778f720dca8a1;hb=0652bf3563918ba890a7eb4a6c20bd7d78938832;hp=0000000000000000000000000000000000000000;hpb=0fe3e21f79b56e7a145af40eb4aa88add2ad7636;p=packages%2Fqemu.git diff --git a/qemu-user-execve.patch b/qemu-user-execve.patch new file mode 100644 index 0000000..78c0e57 --- /dev/null +++ b/qemu-user-execve.patch @@ -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 +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 +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 +Tested-by: Laurent Vivier +Reviewed-by: Laurent Vivier +--- +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 + #include + #include + +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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_. */ +@@ -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;