From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39641) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bF5RP-0007gT-VQ for qemu-devel@nongnu.org; Mon, 20 Jun 2016 16:05:08 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bF5RL-0000tE-La for qemu-devel@nongnu.org; Mon, 20 Jun 2016 16:05:03 -0400 Received: from mail-it0-x233.google.com ([2607:f8b0:4001:c0b::233]:35210) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bF5RL-0000sy-EU for qemu-devel@nongnu.org; Mon, 20 Jun 2016 16:04:59 -0400 Received: by mail-it0-x233.google.com with SMTP id g127so1632602ith.0 for ; Mon, 20 Jun 2016 13:04:59 -0700 (PDT) References: <1465932382-28645-1-git-send-email-joel.holdsworth@vcatechnology.com> <1465932382-28645-2-git-send-email-joel.holdsworth@vcatechnology.com> <61123e80-fab0-0b7e-f4b1-c6e7ee1bdcbb@vivier.eu> From: Joel Holdsworth Message-ID: <57684C69.1020605@vcatechnology.com> Date: Mon, 20 Jun 2016 21:04:57 +0100 MIME-Version: 1.0 In-Reply-To: <61123e80-fab0-0b7e-f4b1-c6e7ee1bdcbb@vivier.eu> Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit Subject: Re: [Qemu-devel] [PATCH v2 1/4] linux-user: add option to intercept execve() syscalls List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Laurent Vivier , qemu-devel@nongnu.org Cc: riku.voipio@iki.fi, Vasileios.Kalintiris@imgtec.com, Petros Angelatos On 15/06/16 20:31, Laurent Vivier wrote: > > Le 14/06/2016 à 21:26, Joel Holdsworth a écrit : >> From: Petros Angelatos >> >> 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 >> --- >> 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 >> #include >> #include >> #include >> @@ -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 >> #endif >> #include >> +#include >> #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 Thanks - now fixed on my branch. I will resubmit when the discussion has died down. Joel