#include "config.h"
-
void ML_(guess_and_register_stack) (Addr sp, ThreadState* tst)
{
Bool debug = False;
vg_assert(i_am_the_only_thread());
}
-// XXX: prototype here seemingly doesn't match the prototype for i386-linux,
-// but it seems to work nonetheless...
-PRE(sys_execve)
+/* This handles the common part of the PRE macro for execve and execveat. */
+void handle_pre_sys_execve(ThreadId tid, SyscallStatus *status, Addr pathname,
+ Addr arg_2, Addr arg_3, Bool is_execveat,
+ Bool check_pathptr)
{
HChar* path = NULL; /* path to executable */
HChar** envp = NULL;
Int i, j, tot_args;
SysRes res;
Bool setuid_allowed, trace_this_child;
+ const char *str;
+ char str2[30], str3[30];
- PRINT("sys_execve ( %#" FMT_REGWORD "x(%s), %#" FMT_REGWORD "x, %#"
- FMT_REGWORD "x )", ARG1, (HChar*)(Addr)ARG1, ARG2, ARG3);
- PRE_REG_READ3(vki_off_t, "execve",
- char *, filename, char **, argv, char **, envp);
- PRE_MEM_RASCIIZ( "execve(filename)", ARG1 );
- if (ARG2 != 0) {
- /* At least the terminating NULL must be addressable. */
- if (!ML_(safe_to_deref)((HChar **) (Addr)ARG2, sizeof(HChar *))) {
+ if (is_execveat)
+ str = "execveat";
+ else
+ str = "execve";
+
+ VG_(strcpy)(str2, str);
+ VG_(strcpy)(str3, str);
+
+ if (arg_2 != 0) {
+ /* At least the terminating NULL must be addressable. */
+ if (!ML_(safe_to_deref)((HChar **) (Addr)arg_2, sizeof(HChar *))) {
SET_STATUS_Failure(VKI_EFAULT);
return;
}
- ML_(pre_argv_envp)( ARG2, tid, "execve(argv)", "execve(argv[i])" );
+ VG_(strcat)(str2, "(argv)");
+ VG_(strcat)(str3, "(argv[i])");
+ ML_(pre_argv_envp)( arg_2, tid, str2, str3 );
}
- if (ARG3 != 0) {
+ // Reset helper strings to syscall name.
+ str2[VG_(strlen)(str)] = '\0';
+ str3[VG_(strlen)(str)] = '\0';
+ if (arg_3 != 0) {
/* At least the terminating NULL must be addressable. */
- if (!ML_(safe_to_deref)((HChar **) (Addr)ARG3, sizeof(HChar *))) {
+ if (!ML_(safe_to_deref)((HChar **) (Addr)arg_3, sizeof(HChar *))) {
SET_STATUS_Failure(VKI_EFAULT);
return;
}
- ML_(pre_argv_envp)( ARG3, tid, "execve(envp)", "execve(envp[i])" );
+ VG_(strcat)(str2, "(envp)");
+ VG_(strcat)(str3, "(envp[i])");
+ ML_(pre_argv_envp)( arg_3, tid, str2, str3 );
}
vg_assert(VG_(is_valid_tid)(tid));
an effort to check that the execve will work before actually
doing it. */
- /* Check that the name at least begins in client-accessible storage. */
- if (ARG1 == 0 /* obviously bogus */
- || !VG_(am_is_valid_for_client)( ARG1, 1, VKI_PROT_READ )) {
- SET_STATUS_Failure( VKI_EFAULT );
- return;
+ /* Check that the name at least begins in client-accessible storage.
+ If we didn't create it ourselves in execveat. */
+ if (check_pathptr
+ && !VG_(am_is_valid_for_client)( pathname, 1, VKI_PROT_READ )) {
+ SET_STATUS_Failure( VKI_EFAULT );
+ return;
}
// debug-only printing
if (0) {
- VG_(printf)("ARG1 = %p(%s)\n", (void*)(Addr)ARG1, (HChar*)(Addr)ARG1);
- if (ARG2) {
- VG_(printf)("ARG2 = ");
+ VG_(printf)("pathname = %p(%s)\n", (void*)(Addr)pathname, (HChar*)(Addr)pathname);
+ if (arg_2) {
+ VG_(printf)("arg_2 = ");
Int q;
- HChar** vec = (HChar**)(Addr)ARG2;
+ HChar** vec = (HChar**)(Addr)arg_2;
for (q = 0; vec[q]; q++)
VG_(printf)("%p(%s) ", vec[q], vec[q]);
VG_(printf)("\n");
} else {
- VG_(printf)("ARG2 = null\n");
+ VG_(printf)("arg_2 = null\n");
}
}
// Decide whether or not we want to follow along
{ // Make 'child_argv' be a pointer to the child's arg vector
// (skipping the exe name)
- const HChar** child_argv = (const HChar**)(Addr)ARG2;
+ const HChar** child_argv = (const HChar**)(Addr)arg_2;
if (child_argv && child_argv[0] == NULL)
child_argv = NULL;
- trace_this_child = VG_(should_we_trace_this_child)( (HChar*)(Addr)ARG1,
+ trace_this_child = VG_(should_we_trace_this_child)( (HChar*)(Addr)pathname,
child_argv );
}
// ok, etc. We allow setuid executables to run only in the case when
// we are not simulating them, that is, they to be run natively.
setuid_allowed = trace_this_child ? False : True;
- res = VG_(pre_exec_check)((const HChar *)(Addr)ARG1, NULL, setuid_allowed);
+ res = VG_(pre_exec_check)((const HChar *)(Addr)pathname, NULL, setuid_allowed);
if (sr_isError(res)) {
SET_STATUS_Failure( sr_Err(res) );
return;
}
/* After this point, we can't recover if the execve fails. */
- VG_(debugLog)(1, "syswrap", "Exec of %s\n", (HChar*)(Addr)ARG1);
+ VG_(debugLog)(1, "syswrap", "Exec of %s\n", (HChar*)(Addr)pathname);
// Terminate gdbserver if it is active.
}
} else {
- path = (HChar*)(Addr)ARG1;
+ path = (HChar*)(Addr)pathname;
}
// Set up the child's environment.
//
// Then, if tracing the child, set VALGRIND_LIB for it.
//
- if (ARG3 == 0) {
+ if (arg_3 == 0) {
envp = NULL;
} else {
- envp = VG_(env_clone)( (HChar**)(Addr)ARG3 );
+ envp = VG_(env_clone)( (HChar**)(Addr)arg_3 );
if (envp == NULL) goto hosed;
VG_(env_remove_valgrind_env_stuff)( envp, True /*ro_strings*/, NULL );
}
if (trace_this_child) {
- // Set VALGRIND_LIB in ARG3 (the environment)
+ // Set VALGRIND_LIB in arg_3 (the environment)
VG_(env_setenv)( &envp, VALGRIND_LIB, VG_(libdir));
}
// Set up the child's args. If not tracing it, they are
- // simply ARG2. Otherwise, they are
+ // simply arg_2. Otherwise, they are
//
- // [launcher_basename] ++ VG_(args_for_valgrind) ++ [ARG1] ++ ARG2[1..]
+ // [launcher_basename] ++ VG_(args_for_valgrind) ++ [pathname] ++ arg_2[1..]
//
// except that the first VG_(args_for_valgrind_noexecpass) args
// are omitted.
//
if (!trace_this_child) {
- argv = (HChar**)(Addr)ARG2;
+ argv = (HChar**)(Addr)arg_2;
} else {
vg_assert( VG_(args_for_valgrind) );
vg_assert( VG_(args_for_valgrind_noexecpass) >= 0 );
// name of client exe
tot_args++;
// args for client exe, skipping [0]
- arg2copy = (HChar**)(Addr)ARG2;
+ arg2copy = (HChar**)(Addr)arg_2;
if (arg2copy && arg2copy[0]) {
for (i = 1; arg2copy[i]; i++)
tot_args++;
continue;
argv[j++] = * (HChar**) VG_(indexXA)( VG_(args_for_valgrind), i );
}
- argv[j++] = (HChar*)(Addr)ARG1;
+ argv[j++] = (HChar*)(Addr)pathname;
if (arg2copy && arg2copy[0])
for (i = 1; arg2copy[i]; i++)
argv[j++] = arg2copy[i];
VG_(printf)("env: %s\n", *cpp);
}
+ // always execute this because it's executing valgrind, not the "target" exe
SET_STATUS_from_SysRes(
- VG_(do_syscall3)(__NR_execve, (UWord)path, (UWord)argv, (UWord)envp)
- );
+ VG_(do_syscall3)(__NR_execve, (UWord)path, (UWord)argv, (UWord)envp));
/* If we got here, then the execve failed. We've already made way
too much of a mess to continue, so we have to abort. */
vg_assert(FAILURE);
VG_(message)(Vg_UserMsg, "execve(%#" FMT_REGWORD "x(%s), %#" FMT_REGWORD
"x, %#" FMT_REGWORD "x) failed, errno %lu\n",
- ARG1, (HChar*)(Addr)ARG1, ARG2, ARG3, ERR);
+ pathname, (HChar*)(Addr)pathname, arg_2, arg_3, ERR);
VG_(message)(Vg_UserMsg, "EXEC FAILED: I can't recover from "
"execve() failing, so I'm dying.\n");
VG_(message)(Vg_UserMsg, "Add more stringent tests in PRE(sys_execve), "
"or work out how to recover.\n");
VG_(exit)(101);
+
+}
+
+// XXX: prototype here seemingly doesn't match the prototype for i386-linux,
+// but it seems to work nonetheless...
+PRE(sys_execve)
+{
+ PRINT("sys_execve ( %#" FMT_REGWORD "x(%s), %#" FMT_REGWORD "x, %#"
+ FMT_REGWORD "x )", ARG1, (HChar*)(Addr)ARG1, ARG2, ARG3);
+ PRE_REG_READ3(vki_off_t, "execve",
+ char *, filename, char **, argv, char **, envp);
+ PRE_MEM_RASCIIZ( "execve(filename)", ARG1 );
+
+ char *pathname = (char *)ARG1;
+ Addr arg_2 = (Addr)ARG2;
+ Addr arg_3 = (Addr)ARG3;
+
+ handle_pre_sys_execve(tid, status, (Addr)pathname, arg_2, arg_3, 0, True);
}
PRE(sys_access)
{
}
+PRE(sys_execveat)
+{
+ PRINT("sys_execveat ( %lu, %#lx(%s), %#lx, %#lx, %lu", ARG1, ARG2, (char*)ARG2, ARG3, ARG4, ARG5);
+ PRE_REG_READ5(vki_off_t, "execveat",
+ int, fd, char *, filename, char **, argv, char **, envp, int, flags);
+ PRE_MEM_RASCIIZ( "execveat(filename)", ARG2);
+
+#if !defined(__NR_execveat)
+ SET_STATUS_Failure(VKI_ENOSYS);
+ return;
+#endif
+
+ char *path = (char*) ARG2;
+ Addr arg_2 = ARG3;
+ Addr arg_3 = ARG4;
+ const HChar *buf;
+ HChar *abs_path = NULL;
+ Bool check_at_symlink = False;
+ Bool check_pathptr = True;
+
+ if (ML_(safe_to_deref) (path, 1)) {
+ /* If pathname is absolute, we'll ignore dirfd
+ * and just pass the pathname, try to determine
+ * the absolute path otherwise. */
+ if (path[0] != '/') {
+ /* Check dirfd is a valid fd. */
+ if (!ML_(fd_allowed)(ARG1, "execveat", tid, False)) {
+ SET_STATUS_Failure( VKI_EBADF );
+ return;
+ }
+ /* If pathname is empty and AT_EMPTY_PATH is
+ set then dirfd describes the whole path. */
+ if (path[0] == '\0') {
+ if (ARG5 & VKI_AT_EMPTY_PATH) {
+ if (VG_(resolve_filename)(ARG1, &buf)) {
+ VG_(strcpy)(path, buf);
+ check_pathptr = False;
+ }
+ }
+ }
+ else if (ARG1 == VKI_AT_FDCWD) {
+ check_at_symlink = True;
+ } else
+ if (ARG5 & VKI_AT_SYMLINK_NOFOLLOW)
+ check_at_symlink = True;
+ else if (VG_(resolve_filename)(ARG1, &buf)) {
+ abs_path = VG_(malloc)("execveat",
+ (VG_(strlen)(buf) + 1
+ + VG_(strlen)(path) + 1));
+ VG_(sprintf)(abs_path, "%s/%s", buf, path);
+ path = abs_path;
+ check_pathptr = False;
+ }
+ else
+ path = NULL;
+ if (check_at_symlink) {
+ struct vg_stat statbuf;
+ SysRes statres;
+
+ statres = VG_(stat)(path, &statbuf);
+ if (sr_isError(statres) || VKI_S_ISLNK(statbuf.mode)) {
+ SET_STATUS_Failure( VKI_ELOOP );
+ return;
+ }
+ }
+ }
+ } else {
+ SET_STATUS_Failure(VKI_EFAULT);
+ return;
+ }
+
+ handle_pre_sys_execve(tid, status, (Addr) path, arg_2, arg_3, 1,
+ check_pathptr);
+
+ /* The exec failed, we keep running... cleanup. */
+ VG_(free)(abs_path);
+
+
+}
+
+
#undef PRE
#undef POST
--- /dev/null
+#include <sys/syscall.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static int sys_execveat (int dirfd, const char *pathname,
+ char *const argv[], char *const envp[],
+ int flags)
+{
+#if defined(__NR_execveat)
+ return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags);
+#else
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+
+int main()
+{
+ char *argv[] = { "foobar", "execveat exists", NULL };
+ char *envp[] = { NULL };
+ DIR *dirp;
+ int fd;
+
+ dirp = opendir("/bin");
+ if (dirp == NULL) {
+ perror("execveat");
+ exit(EXIT_FAILURE);
+ }
+ fd = dirfd(dirp);
+
+ /* Check valgrind will produce expected warnings for the
+ various wrong arguments. */
+ do {
+ char *mem = malloc(16);
+ void *t = (void *) &mem[0];
+ void *z = (void *) -1;
+ int flag = *((int *) &mem[8]);
+
+ sys_execveat(-1, "bin/xecho", argv, envp, 0);
+ sys_execveat(-1, "xecho", argv, envp, 0);
+ sys_execveat(fd, "xecho", argv, envp, flag);
+ sys_execveat(fd, "", argv, envp, 0);
+ sys_execveat(fd, NULL, argv, envp, 0);
+ sys_execveat(fd, "xecho", t, envp, 0);
+ sys_execveat(fd, "xecho", z, envp, 0);
+ } while (0);
+
+ /* Check execveat called with the correct arguments works. */
+ if (sys_execveat(fd, "echo", argv, envp, 0) == -1) {
+ perror("execveat");
+ exit(EXIT_FAILURE);
+ }
+
+ closedir(dirp);
+ exit(EXIT_SUCCESS);
+}
+