Implement fexecve and a few testcases on FreeBSD.
/memcheck/tests/freebsd/get_set_context
/memcheck/tests/freebsd/utimes
/memcheck/tests/freebsd/static_allocs
+/memcheck/tests/freebsd/fexecve
# /memcheck/tests/amd64-freebsd
/memcheck/tests/amd64-freebsd/*.stderr.diff
/none/tests/freebsd/auxv
/none/tests/freebsd/osrel
/none/tests/freebsd/swapcontext
+/none/tests/freebsd/fexecve
+/none/tests/freebsd/hello_world
# /none/tests/x86/
/none/tests/x86/*.dSYM
# endif
}
+#if defined(VGO_freebsd)
+
+/* This should only be called after a successful call to
+ * Bool VG_(resolve_filename) ( Int fd, const HChar** result )
+ * so that filedesc_buf is still valid for fd */
+Bool VG_(resolve_filemode) ( Int fd, Int * result )
+{
+ Char *bp, *eb;
+ struct vki_kinfo_file *kf;
+
+ /* Walk though the list. */
+ bp = filedesc_buf;
+ eb = filedesc_buf + sizeof(filedesc_buf);
+ while (bp < eb) {
+ kf = (struct vki_kinfo_file *)bp;
+ if (kf->kf_fd == fd)
+ break;
+ bp += kf->kf_structsize;
+ }
+ if (bp >= eb)
+ *result = -1;
+ else
+ *result = kf->kf_flags;
+ return True;
+}
+#endif
+
+
SysRes VG_(mknod) ( const HChar* pathname, Int mode, UWord dev )
{
# if defined(VGP_arm64_linux) || defined(VGP_nanomips_linux)
Int fd, Addr vector, Int count,
const char *str);
+typedef enum {
+ EXECVE,
+ EXECVEAT,
+ FEXECVE
+} ExecveType;
+
extern
void handle_pre_sys_execve(ThreadId tid, SyscallStatus *status, Addr pathname,
- Addr arg_2, Addr arg_3, Bool is_execveat,
+ Addr arg_2, Addr arg_3, ExecveType execveType,
Bool check_pathptr);
DECL_TEMPLATE(generic, sys_ni_syscall); // * P -- unimplemented
{
PRINT("sys_fexecve ( %" FMT_REGWORD "d, %#" FMT_REGWORD "x, %#" FMT_REGWORD "x )",
SARG1,ARG2,ARG3);
- PRE_REG_READ3(long, "fexecve",
+ PRE_REG_READ3(int, "fexecve",
int, fd, char * const *, argv,
char * const *, envp);
- PRE_MEM_RASCIIZ( "fexecve(argv)", ARG2 );
- PRE_MEM_RASCIIZ( "fexecve(envp)", ARG3 );
+
+ if (!ML_(fd_allowed)(ARG1, "fexecve", tid, False)) {
+ SET_STATUS_Failure(VKI_EBADF);
+ return;
+ }
+
+ const HChar *fname;
+
+ if (VG_(resolve_filename)(ARG1, &fname) == False) {
+ SET_STATUS_Failure(VKI_ENOENT);
+ return;
+ }
+
+ struct vg_stat stats;
+ if (VG_(fstat)(ARG1, &stats) != 0) {
+ SET_STATUS_Failure(VKI_EACCES);
+ return;
+ }
+
+ Int openFlags;
+
+ if (VG_(resolve_filemode)(ARG1, &openFlags) == False) {
+ SET_STATUS_Failure(VKI_ENOENT);
+ return;
+ }
+
+ /*
+ * openFlags is in kernel FFLAGS format
+ * (see /usr/include/sys/fcntl.h)
+ * which alllows us to tell if RDONLY is set
+ *
+ */
+
+ Bool isScript = False;
+
+ SysRes res;
+ res = VG_(open)(fname, VKI_O_RDONLY,
+ VKI_S_IRUSR|VKI_S_IRGRP|VKI_S_IROTH);
+ if (sr_isError(res)) {
+ SET_STATUS_Failure(VKI_ENOENT);
+ return;
+ } else {
+ char buf[2];
+ VG_(read)((Int)sr_Res(res), buf, 2);
+ VG_(close)((Int)sr_Res(res));
+ if (buf[0] == '#' && buf[1] == '!')
+ {
+ isScript = True;
+ }
+ }
+
+ if (isScript) {
+ if (!(openFlags & VKI_FREAD)) {
+ SET_STATUS_Failure(VKI_EACCES);
+ return;
+ }
+ } else {
+ if (!((openFlags & VKI_O_EXEC) ||
+ (stats.mode & (VKI_S_IXUSR|VKI_S_IXGRP|VKI_S_IXOTH)))) {
+ SET_STATUS_Failure(VKI_EACCES);
+ return;
+ }
+ }
+
+ Addr arg_2 = (Addr)ARG2;
+ Addr arg_3 = (Addr)ARG3;
+
+ handle_pre_sys_execve(tid, status, (Addr)fname, arg_2, arg_3, FEXECVE, False);
}
// SYS_freebsd11_fstatat 493
/* 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,
+ Addr arg_2, Addr arg_3, ExecveType execveType,
Bool check_pathptr)
{
HChar* path = NULL; /* path to executable */
const char *str;
char str2[30], str3[30];
- if (is_execveat)
- str = "execveat";
- else
- str = "execve";
+ switch (execveType) {
+ case EXECVE:
+ str = "execve";
+ break;
+ case EXECVEAT:
+ str = "execveat";
+ break;
+ case FEXECVE:
+ str = "fexecve";
+ break;
+ default:
+ vg_assert(False);
+ }
VG_(strcpy)(str2, str);
VG_(strcpy)(str3, str);
Addr arg_2 = (Addr)ARG2;
Addr arg_3 = (Addr)ARG3;
- handle_pre_sys_execve(tid, status, (Addr)pathname, arg_2, arg_3, 0, True);
+ handle_pre_sys_execve(tid, status, (Addr)pathname, arg_2, arg_3, EXECVE, True);
}
PRE(sys_access)
return;
}
- handle_pre_sys_execve(tid, status, (Addr) path, arg_2, arg_3, 1,
+ handle_pre_sys_execve(tid, status, (Addr) path, arg_2, arg_3, EXECVEAT,
check_pathptr);
/* The exec failed, we keep running... cleanup. */
/* Convert an fd into a filename */
extern Bool VG_(resolve_filename) ( Int fd, const HChar** buf );
+#if defined(VGO_freebsd)
+/* get the flags used to obtain an fd */
+extern Bool VG_(resolve_filemode) ( Int fd, Int * result );
+#endif
+
/* Return the size of a file, or -1 in case of error */
extern Long VG_(fsize) ( Int fd );
#define VKI_O_WRONLY O_WRONLY
#define VKI_O_RDWR O_RDWR
+#define VKI_FREAD FREAD
+#define VKI_WRITE WRITE
+
#define VKI_O_NONBLOCK O_NONBLOCK
#define VKI_O_APPEND O_APPEND
#define VKI_O_CREAT O_CREAT
#define VKI_O_TRUNC O_TRUNC
#define VKI_O_EXCL O_EXCL
+#define VKI_O_DIRECTORY O_DIRECTORY
+#define VKI_O_EXEC O_EXEC
+#define VKI_O_SEARCH O_EXEC
#define VKI_AT_FDCWD AT_FDCWD
utimes.stderr.exp-x86 \
utimes.stderr.exp \
static_allocs.vgtest \
- static_allocs.stderr.exp
+ static_allocs.stderr.exp \
+ fexecve.vgtest \
+ fexecve.stderr.exp
check_PROGRAMS = \
statfs pdfork_pdkill getfsstat inlinfo inlinfo_nested.so extattr \
sigwait chflags get_set_login revoke scalar capsicum getfh \
linkat scalar_fork scalar_thr_exit scalar_abort2 scalar_pdfork \
scalar_vfork stat file_locking_wait6 utimens access chmod_chown \
- misc get_set_context utimes static_allocs
+ misc get_set_context utimes static_allocs fexecve
AM_CFLAGS += $(AM_FLAG_M3264_PRI)
AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
--- /dev/null
+#include <fcntl.h> // open
+#include <stdio.h> // perror
+#include <string.h> // strdup
+#include <stdlib.h> // exit
+#include <unistd.h> // fexecve
+
+int main(int argc, char **argv, char** envp)
+{
+ char *exe = "/usr/bin/true";
+
+ int fd = open(exe, O_RDONLY);
+ if (-1 == fd)
+ {
+ perror("open failed:");
+ exit(-1);
+ }
+ char ** new_argv = malloc(2*sizeof(char *));
+ char ** new_envp = malloc(2*sizeof(char *));
+ char * arg1 = strdup("./fexecve");
+ char * env1 = strdup("FOO=bar");
+ int * new_fd = malloc(sizeof(int));
+ *new_fd += fd;
+ new_argv[1] = new_envp[1] = NULL;
+ argv[0] = arg1;
+ envp[0] = env1;
+
+ free(arg1);
+ free(env1);
+ if (-1 == fexecve(*new_fd, new_argv, new_envp))
+ {
+ perror("fexecv failed:");
+ exit(-1);
+ }
+}
--- /dev/null
+Syscall param fexecve(fd) contains uninitialised byte(s)
+ at 0x........: fexecve (in /...libc...)
+ by 0x........: main (fexecve.c:29)
+
+Syscall param fexecve(argv) points to uninitialised byte(s)
+ at 0x........: fexecve (in /...libc...)
+ by 0x........: main (fexecve.c:29)
+ Address 0x........ is 0 bytes inside a block of size 16 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (fexecve.c:17)
+
+Syscall param fexecve(envp) points to uninitialised byte(s)
+ at 0x........: fexecve (in /...libc...)
+ by 0x........: main (fexecve.c:29)
+ Address 0x........ is 0 bytes inside a block of size 16 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (fexecve.c:18)
+
--- /dev/null
+prog: fexecve
+vgopts: -q
Syscall param fexecve(envp) contains uninitialised byte(s)
...
-Syscall param fexecve(argv) points to unaddressable byte(s)
- ...
- Address 0x........ is not stack'd, malloc'd or (recently) free'd
-
-Syscall param fexecve(envp) points to unaddressable byte(s)
- ...
- Address 0x........ is not stack'd, malloc'd or (recently) free'd
-
---------------------------------------------------------
493: SYS_freebsd11_fstatat 4s 2m
---------------------------------------------------------
Syscall param fexecve(envp) contains uninitialised byte(s)
...
-Syscall param fexecve(argv) points to unaddressable byte(s)
- ...
- Address 0x........ is not stack'd, malloc'd or (recently) free'd
-
-Syscall param fexecve(envp) points to unaddressable byte(s)
- ...
- Address 0x........ is not stack'd, malloc'd or (recently) free'd
-
---------------------------------------------------------
493: SYS_freebsd11_fstatat 4s 2m
---------------------------------------------------------
Syscall param fexecve(envp) contains uninitialised byte(s)
...
-Syscall param fexecve(argv) points to unaddressable byte(s)
- ...
- Address 0x........ is not stack'd, malloc'd or (recently) free'd
-
-Syscall param fexecve(envp) points to unaddressable byte(s)
- ...
- Address 0x........ is not stack'd, malloc'd or (recently) free'd
-
---------------------------------------------------------
493: SYS_freebsd11_fstatat 4s 2m
---------------------------------------------------------
Syscall param fexecve(envp) contains uninitialised byte(s)
...
-Syscall param fexecve(argv) points to unaddressable byte(s)
- ...
- Address 0x........ is not stack'd, malloc'd or (recently) free'd
-
-Syscall param fexecve(envp) points to unaddressable byte(s)
- ...
- Address 0x........ is not stack'd, malloc'd or (recently) free'd
-
---------------------------------------------------------
493: SYS_freebsd11_fstatat 4s 2m
---------------------------------------------------------
include $(top_srcdir)/Makefile.tool-tests.am
-dist_noinst_SCRIPTS = filter_stderr
+dist_noinst_SCRIPTS = filter_stderr test.sh
EXTRA_DIST = \
auxv.vgtest \
auxv.stderr.exp \
osrel.stdout.exp \
swapcontext.vgtest \
swapcontext.stderr.exp \
- swapcontext.stdout.exp
+ swapcontext.stdout.exp \
+ fexecve_hw1.vgtest \
+ fexecve_hw1.stdout.exp \
+ fexecve_hw1.stderr.exp \
+ fexecve_hw2.vgtest \
+ fexecve_hw2.stdout.exp \
+ fexecve_hw2.stderr.exp \
+ fexecve_script1.vgtest \
+ fexecve_script1.stderr.exp \
+ fexecve_script2.vgtest \
+ fexecve_script2.stdout.exp \
+ fexecve_script2.stderr.exp \
+ fexecve_txt.vgtest \
+ fexecve_txt.stderr.exp
check_PROGRAMS = \
- auxv osrel swapcontext
+ auxv osrel swapcontext hello_world fexecve
AM_CFLAGS += $(AM_FLAG_M3264_PRI)
AM_CXXFLAGS += $(AM_FLAG_M3264_PRI)
osrel_CFLAGS = ${AM_CFLAGS}
swapcontext_CFLAGS = ${AM_CFLAGS}
+hello_world_SOURCES = hello_world.cpp
--- /dev/null
+#include <fcntl.h> // open
+#include <stdio.h> // perror
+#include <unistd.h> // getopt
+#include <stdlib.h> // exit
+
+int main(int argc, char **argv, char** envp)
+{
+ char *exe = "./hello_world";
+ int open_flags = 0;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "erst")) != -1)
+ {
+ switch (opt)
+ {
+ case 'e':
+ open_flags |= O_EXEC;
+ break;
+ case 'r':
+ open_flags |= O_RDONLY;
+ break;
+ case 's':
+ exe = "./test.sh";
+ break;
+ case 't':
+ exe = "./fexecve.c";
+ break;
+ default:
+ fprintf(stderr, "bad usage, options are\n"
+ "\texec flag\t-e\n"
+ "\trdonly flag\t-r\n"
+ "\texec script\t-s\n"
+ "\ntext file\n-t");
+ exit(-1);
+ }
+ }
+
+ int fd = open(exe, open_flags);
+ if (-1 == fd)
+ {
+ perror("open failed:");
+ exit(-1);
+ }
+ char *new_argv[] = {
+ exe,
+ NULL
+ };
+ if (-1 == fexecve(fd, new_argv, envp))
+ {
+ perror("fexecv failed:");
+ exit(-1);
+ }
+}
--- /dev/null
+Compiled Hello, World!
--- /dev/null
+prereq: test -e hello_world
+prog: fexecve
+args: -r -e
+vgopts: -q
+
--- /dev/null
+Compiled Hello, World!
--- /dev/null
+prereq: test -e hello_world
+prog: fexecve
+args: -e
+vgopts: -q
+
--- /dev/null
+fexecv failed:: Permission denied
--- /dev/null
+prog: fexecve
+args: -r -e -s
+vgopts: -q
+
--- /dev/null
+Script Hello, World!
--- /dev/null
+prog: fexecve
+args: -r -s
+vgopts: -q
+
--- /dev/null
+fexecv failed:: Permission denied
--- /dev/null
+prog: fexecve
+args: -r -t
+vgopts: -q
+
--- /dev/null
+#include <iostream>
+
+int main()
+{
+ std::cout << "Compiled Hello, World!\n";
+}
--- /dev/null
+#!/bin/sh
+echo Script Hello, World!