[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v2 1/4] linux-user: add option to intercept exec
From: |
Laurent Vivier |
Subject: |
Re: [Qemu-devel] [PATCH v2 1/4] linux-user: add option to intercept execve() syscalls |
Date: |
Wed, 15 Jun 2016 21:31:16 +0200 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.1.0 |
Le 14/06/2016 à 21:26, Joel Holdsworth a écrit :
> From: Petros Angelatos <address@hidden>
>
> 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 <address@hidden>
> Tested-by: Laurent Vivier <address@hidden>
> Reviewed-by: Laurent Vivier <address@hidden>
> ---
> linux-user/main.c | 37 ++++++++++++++
> linux-user/qemu.h | 1 +
> linux-user/syscall.c | 137
> ++++++++++++++++++++++++++++++++++++++++++++++-----
> 3 files changed, 164 insertions(+), 11 deletions(-)
>
> diff --git a/linux-user/main.c b/linux-user/main.c
> index f8a8764..429dc06 100644
> --- a/linux-user/main.c
> +++ b/linux-user/main.c
> @@ -18,6 +18,8 @@
> */
> #include "qemu/osdep.h"
> #include "qemu-version.h"
> +
> +#include <sys/auxv.h>
> #include <sys/mman.h>
> #include <sys/syscall.h>
> #include <sys/resource.h>
> @@ -79,6 +81,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
> @@ -3947,6 +3950,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;
> @@ -4032,6 +4067,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 56f29c3..567bcc1 100644
> --- a/linux-user/qemu.h
> +++ b/linux-user/qemu.h
> @@ -149,6 +149,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. */
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 71ccbd9..a478f56 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -106,6 +106,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
> #include <linux/rtnetlink.h>
> #endif
> #include <linux/audit.h>
> +#include <linux/binfmts.h>
> #include "linux_loop.h"
> #include "uname.h"
>
> @@ -6659,6 +6660,128 @@ 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));
You need a safe_execve() here, too.
Laurent