Bug 444925 fexecve syscall wrapper not properly implemented

Implement fexecve and a few testcases on FreeBSD.
This commit is contained in:
Paul Floyd 2021-11-14 22:06:14 +01:00
parent 5f8211aa60
commit 83dda2b71a
33 changed files with 297 additions and 48 deletions

3
.gitignore vendored
View File

@ -1330,6 +1330,7 @@
/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
@ -2051,6 +2052,8 @@
/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

View File

@ -179,6 +179,34 @@ Bool VG_(resolve_filename) ( Int fd, const HChar** result )
# 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)

View File

@ -127,9 +127,15 @@ void handle_sys_pwritev(ThreadId tid, SyscallStatus* status,
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

View File

@ -4898,11 +4898,77 @@ PRE(sys_fexecve)
{
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

View File

@ -2919,7 +2919,7 @@ void VG_(reap_threads)(ThreadId self)
/* 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 */
@ -2934,10 +2934,19 @@ void handle_pre_sys_execve(ThreadId tid, SyscallStatus *status, Addr pathname,
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);
@ -3230,7 +3239,7 @@ PRE(sys_execve)
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)

View File

@ -13332,7 +13332,7 @@ PRE(sys_execveat)
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. */

View File

@ -44,6 +44,11 @@ extern Int VG_(fcntl) ( Int fd, Int cmd, Addr arg );
/* 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 );

View File

@ -1536,11 +1536,17 @@ struct vki_dirent {
#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

View File

@ -71,14 +71,16 @@ EXTRA_DIST = \
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)

View File

@ -0,0 +1,34 @@
#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);
}
}

View File

@ -0,0 +1,18 @@
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)

View File

@ -0,0 +1,2 @@
prog: fexecve
vgopts: -q

View File

@ -3941,14 +3941,6 @@ Syscall param fexecve(argv) contains uninitialised byte(s)
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
---------------------------------------------------------

View File

@ -3941,14 +3941,6 @@ Syscall param fexecve(argv) contains uninitialised byte(s)
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
---------------------------------------------------------

View File

@ -3972,14 +3972,6 @@ Syscall param fexecve(argv) contains uninitialised byte(s)
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
---------------------------------------------------------

View File

@ -3972,14 +3972,6 @@ Syscall param fexecve(argv) contains uninitialised byte(s)
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
---------------------------------------------------------

View File

@ -1,7 +1,7 @@
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 \
@ -12,10 +12,23 @@ EXTRA_DIST = \
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)
@ -24,3 +37,4 @@ auxv_CFLAGS = ${AM_CFLAGS}
osrel_CFLAGS = ${AM_CFLAGS}
swapcontext_CFLAGS = ${AM_CFLAGS}
hello_world_SOURCES = hello_world.cpp

View File

@ -0,0 +1,53 @@
#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);
}
}

View File

@ -0,0 +1 @@
Compiled Hello, World!

View File

@ -0,0 +1,5 @@
prereq: test -e hello_world
prog: fexecve
args: -r -e
vgopts: -q

View File

@ -0,0 +1 @@
Compiled Hello, World!

View File

@ -0,0 +1,5 @@
prereq: test -e hello_world
prog: fexecve
args: -e
vgopts: -q

View File

@ -0,0 +1 @@
fexecv failed:: Permission denied

View File

@ -0,0 +1,4 @@
prog: fexecve
args: -r -e -s
vgopts: -q

View File

@ -0,0 +1 @@
Script Hello, World!

View File

@ -0,0 +1,4 @@
prog: fexecve
args: -r -s
vgopts: -q

View File

@ -0,0 +1 @@
fexecv failed:: Permission denied

View File

@ -0,0 +1,4 @@
prog: fexecve
args: -r -t
vgopts: -q

View File

@ -0,0 +1,6 @@
#include <iostream>
int main()
{
std::cout << "Compiled Hello, World!\n";
}

2
none/tests/freebsd/test.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
echo Script Hello, World!