Skip to content

Instantly share code, notes, and snippets.

@aoleg94
Created July 12, 2016 06:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aoleg94/4ffd65b4085516e5ea948dc150299dd9 to your computer and use it in GitHub Desktop.
Save aoleg94/4ffd65b4085516e5ea948dc150299dd9 to your computer and use it in GitHub Desktop.
From 436f5d97ba3c0ebdfbd233f31751ca2ad5b6feba Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?=
=?UTF-8?q?=D0=BE=D0=B2=20=D0=9E=D0=BB=D0=B5=D0=B3?=
<aoleg94@gmail.com>
Date: Tue, 12 Jul 2016 11:34:12 +0500
Subject: [PATCH] execve patch series
---
linux-user/main.c | 36 +++++++++++
linux-user/qemu.h | 1 +
linux-user/syscall.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 204 insertions(+), 1 deletion(-)
diff --git a/linux-user/main.c b/linux-user/main.c
index 617a179..ac6e017 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -20,6 +20,7 @@
#include "qemu-version.h"
#include <sys/syscall.h>
#include <sys/resource.h>
+#include <sys/auxv.h>
#include "qapi/error.h"
#include "qemu.h"
@@ -79,6 +80,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
@@ -3948,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;
@@ -4033,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 cdf23a7..56c875d 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -154,6 +154,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 8bf6205..28334c9 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -100,6 +100,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
#include <linux/route.h>
#include <linux/filter.h>
#include <linux/blkpg.h>
+#include <linux/binfmts.h>
#include <linux/netlink.h>
#ifdef CONFIG_RTNETLINK
#include <linux/rtnetlink.h>
@@ -6772,6 +6773,169 @@ 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 **qemu_argp, **argp;
+ int i, j;
+ size_t qemu_argc = 5, argc, host_envc, envpc;
+ int fd, ret;
+ char *cp;
+ size_t def_envc = 0, undef_envc = 0;
+ char **def_env, **undef_env;
+ char buf[BINPRM_BUF_SIZE];
+
+ /* normal execve case */
+ if (qemu_execve_path == NULL || *qemu_execve_path == 0) {
+ return get_errno(execve(filename, argv, envp));
+ }
+
+ /* count the number of arguments and environment variables */
+ for (argc = 0; argv[argc]; argc++);
+ for (host_envc = 0; environ[host_envc]; host_envc++);
+ for (envpc = 0; envp[envpc]; envpc++);
+
+ /* read the file header so we can check the shebang */
+ 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)
+ qemu_argc += 2;
+ else
+ qemu_argc += 1;
+ }
+
+ /* list environment variables to define */
+ def_env = alloca((envpc + 1) * sizeof(envp[0]));
+ for (i = 0; i != envpc; i++) {
+ for (j = 0; j != host_envc; j++)
+ if (!strcmp(envp[i], environ[j]))
+ break;
+ if (j == host_envc)
+ def_env[def_envc++] = envp[i];
+ }
+
+ argc += def_envc * 2;
+
+ /* list environment variables to undefine */
+ undef_env = alloca((host_envc + 1) * sizeof(envp[0]));
+ for (i = 0; i != host_envc; i++) {
+ const char *const host_env = environ[i];
+ const size_t key_len = strchr(host_env, '=') - host_env;
+ for (j = 0; j != envpc; j++)
+ if (!strncmp(host_env, envp[j], key_len))
+ break;
+ if (j == envpc)
+ undef_env[undef_envc++] = strndup(environ[i], key_len);
+ }
+
+ argc += undef_envc * 2;
+
+ /* allocate the argument list */
+ if (do_strace)
+ qemu_argc++;
+ argp = qemu_argp = alloca((qemu_argc + 1) * sizeof(void *));
+
+ /* set up the qemu arguments */
+ *argp++ = strdup(qemu_execve_path);
+ *argp++ = strdup("-L");
+ *argp++ = strdup(path("/"));
+
+ if (do_strace)
+ *argp++ = strdup("-strace");
+
+ /* add arguments for the enironment variables */
+ for (i = 0; i < def_envc; i++) {
+ *argp++ = strdup("-E");
+ *argp++ = def_env[i];
+ }
+
+ for (i = 0; i < undef_envc; i++) {
+ *argp++ = strdup("-U");
+ *argp++ = undef_env[i];
+ }
+
+ /* add the path to the executable */
+ *argp++ = strdup("-0");
+ if (i_name) {
+ *argp++ = i_name;
+ *argp++ = i_name;
+ if (i_arg)
+ *argp++ = i_arg;
+ } else {
+ *argp++ = argv[0];
+ }
+
+ *argp++ = filename;
+
+ /* copy the original arguments with offset */
+ for (i = 1; i < argc; i++)
+ *argp++ = argv[i];
+
+ *argp++ = NULL;
+
+ return get_errno(execv(qemu_execve_path, qemu_argp));
+}
+
/* 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>. */
@@ -7061,7 +7225,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
* before the execve completes and makes it the other
* program's problem.
*/
- ret = get_errno(safe_execve(p, argp, envp));
+ //ret = get_errno(safe_execve(p, argp, envp));+
+ ret = qemu_execve(p, argp, envp);
+
unlock_user(p, arg1, 0);
goto execve_end;
--
2.7.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment