Created
July 12, 2016 06:37
-
-
Save aoleg94/4ffd65b4085516e5ea948dc150299dd9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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?= | |
<[email protected]> | |
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