summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandra Hájková <ahajkova@redhat.com>2020-04-09 17:28:18 +0200
committerMark Wielaard <mark@klomp.org>2020-06-08 20:26:47 +0200
commit3189cbe99adfc8a739684c7735d26a9722d3a627 (patch)
treef54566f8994e420ed6eee44f75af2623084ea815
parentPrepare NEWS for 3.17.0 (diff)
downloadvalgrind-execveat.tar.gz
valgrind-execveat.tar.bz2
valgrind-execveat.tar.xz
Add support for execveat syscallexecveat
Refactor the code to be reusable between execve and execveat syscalls. https://bugs.kde.org/show_bug.cgi?id=345077
-rw-r--r--.gitignore2
-rw-r--r--NEWS1
-rw-r--r--coregrind/m_syswrap/priv_syswrap-generic.h5
-rw-r--r--coregrind/m_syswrap/priv_syswrap-linux.h3
-rw-r--r--coregrind/m_syswrap/syswrap-amd64-linux.c1
-rw-r--r--coregrind/m_syswrap/syswrap-generic.c115
-rw-r--r--coregrind/m_syswrap/syswrap-linux.c81
-rw-r--r--include/vki/vki-linux.h3
-rw-r--r--memcheck/tests/linux/Makefile.am7
-rw-r--r--memcheck/tests/linux/check_execveat.c18
-rw-r--r--memcheck/tests/linux/sys-execveat.c64
-rw-r--r--memcheck/tests/linux/sys-execveat.stderr.exp19
-rw-r--r--memcheck/tests/linux/sys-execveat.stdout.exp1
-rw-r--r--memcheck/tests/linux/sys-execveat.vgtest3
14 files changed, 279 insertions, 44 deletions
diff --git a/.gitignore b/.gitignore
index 085a30b..4a2f070 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1052,6 +1052,8 @@
1052/memcheck/tests/linux/timerfd-syscall 1052/memcheck/tests/linux/timerfd-syscall
1053/memcheck/tests/linux/proc-auxv 1053/memcheck/tests/linux/proc-auxv
1054/memcheck/tests/linux/sys-openat 1054/memcheck/tests/linux/sys-openat
1055/memcheck/tests/linux/sys-execveat
1056/memcheck/tests/linux/check-execveat
1055 1057
1056# /memcheck/tests/mips32/ 1058# /memcheck/tests/mips32/
1057/memcheck/tests/mips32/*.stderr.diff 1059/memcheck/tests/mips32/*.stderr.diff
diff --git a/NEWS b/NEWS
index 2eebd88..9d89f5e 100644
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,7 @@ To see details of a given bug, visit
39 https://bugs.kde.org/show_bug.cgi?id=XXXXXX 39 https://bugs.kde.org/show_bug.cgi?id=XXXXXX
40where XXXXXX is the bug number as listed below. 40where XXXXXX is the bug number as listed below.
41 41
42345077 linux syscall execveat support (linux 3.19)
42n-i-bz helgrind: If hg_cli__realloc fails, return NULL. 43n-i-bz helgrind: If hg_cli__realloc fails, return NULL.
43 44
44Release 3.16.0 (27 May 2020) 45Release 3.16.0 (27 May 2020)
diff --git a/coregrind/m_syswrap/priv_syswrap-generic.h b/coregrind/m_syswrap/priv_syswrap-generic.h
index 73f9224..4717aba 100644
--- a/coregrind/m_syswrap/priv_syswrap-generic.h
+++ b/coregrind/m_syswrap/priv_syswrap-generic.h
@@ -123,6 +123,11 @@ void handle_sys_pwritev(ThreadId tid, SyscallStatus* status,
123 Int fd, Addr vector, Int count, 123 Int fd, Addr vector, Int count,
124 const char *str); 124 const char *str);
125 125
126extern
127void handle_pre_sys_execve(ThreadId tid, SyscallStatus *status, Addr pathname,
128 Addr arg_2, Addr arg_3, Bool is_execveat,
129 Bool check_pathptr);
130
126DECL_TEMPLATE(generic, sys_ni_syscall); // * P -- unimplemented 131DECL_TEMPLATE(generic, sys_ni_syscall); // * P -- unimplemented
127DECL_TEMPLATE(generic, sys_exit); 132DECL_TEMPLATE(generic, sys_exit);
128DECL_TEMPLATE(generic, sys_fork); 133DECL_TEMPLATE(generic, sys_fork);
diff --git a/coregrind/m_syswrap/priv_syswrap-linux.h b/coregrind/m_syswrap/priv_syswrap-linux.h
index 349a97e..cdc73c1 100644
--- a/coregrind/m_syswrap/priv_syswrap-linux.h
+++ b/coregrind/m_syswrap/priv_syswrap-linux.h
@@ -299,6 +299,9 @@ DECL_TEMPLATE(linux, sys_membarrier);
299// Linux-specific (new in Linux 3.18) 299// Linux-specific (new in Linux 3.18)
300DECL_TEMPLATE(linux, sys_bpf); 300DECL_TEMPLATE(linux, sys_bpf);
301 301
302// Linux-specific (new in Linux 3.19)
303DECL_TEMPLATE(linux, sys_execveat);
304
302// Linux-specific (new in Linux 4.11) 305// Linux-specific (new in Linux 4.11)
303DECL_TEMPLATE(linux, sys_statx); 306DECL_TEMPLATE(linux, sys_statx);
304 307
diff --git a/coregrind/m_syswrap/syswrap-amd64-linux.c b/coregrind/m_syswrap/syswrap-amd64-linux.c
index 0aef84a..28d9013 100644
--- a/coregrind/m_syswrap/syswrap-amd64-linux.c
+++ b/coregrind/m_syswrap/syswrap-amd64-linux.c
@@ -856,6 +856,7 @@ static SyscallTableEntry syscall_table[] = {
856 856
857// LIN__(__NR_kexec_file_load, sys_ni_syscall), // 320 857// LIN__(__NR_kexec_file_load, sys_ni_syscall), // 320
858 LINXY(__NR_bpf, sys_bpf), // 321 858 LINXY(__NR_bpf, sys_bpf), // 321
859 LINX_(__NR_execveat, sys_execveat), // 322
859 860
860 LINXY(__NR_preadv2, sys_preadv2), // 327 861 LINXY(__NR_preadv2, sys_preadv2), // 327
861 LINX_(__NR_pwritev2, sys_pwritev2), // 328 862 LINX_(__NR_pwritev2, sys_pwritev2), // 328
diff --git a/coregrind/m_syswrap/syswrap-generic.c b/coregrind/m_syswrap/syswrap-generic.c
index 280c48f..864bda7 100644
--- a/coregrind/m_syswrap/syswrap-generic.c
+++ b/coregrind/m_syswrap/syswrap-generic.c
@@ -65,7 +65,6 @@
65 65
66#include "config.h" 66#include "config.h"
67 67
68
69void ML_(guess_and_register_stack) (Addr sp, ThreadState* tst) 68void ML_(guess_and_register_stack) (Addr sp, ThreadState* tst)
70{ 69{
71 Bool debug = False; 70 Bool debug = False;
@@ -2847,9 +2846,10 @@ void VG_(reap_threads)(ThreadId self)
2847 vg_assert(i_am_the_only_thread()); 2846 vg_assert(i_am_the_only_thread());
2848} 2847}
2849 2848
2850// XXX: prototype here seemingly doesn't match the prototype for i386-linux, 2849/* This handles the common part of the PRE macro for execve and execveat. */
2851// but it seems to work nonetheless... 2850void handle_pre_sys_execve(ThreadId tid, SyscallStatus *status, Addr pathname,
2852PRE(sys_execve) 2851 Addr arg_2, Addr arg_3, Bool is_execveat,
2852 Bool check_pathptr)
2853{ 2853{
2854 HChar* path = NULL; /* path to executable */ 2854 HChar* path = NULL; /* path to executable */
2855 HChar** envp = NULL; 2855 HChar** envp = NULL;
@@ -2860,27 +2860,39 @@ PRE(sys_execve)
2860 Int i, j, tot_args; 2860 Int i, j, tot_args;
2861 SysRes res; 2861 SysRes res;
2862 Bool setuid_allowed, trace_this_child; 2862 Bool setuid_allowed, trace_this_child;
2863 const char *str;
2864 char str2[30], str3[30];
2863 2865
2864 PRINT("sys_execve ( %#" FMT_REGWORD "x(%s), %#" FMT_REGWORD "x, %#" 2866 if (is_execveat)
2865 FMT_REGWORD "x )", ARG1, (HChar*)(Addr)ARG1, ARG2, ARG3); 2867 str = "execveat";
2866 PRE_REG_READ3(vki_off_t, "execve", 2868 else
2867 char *, filename, char **, argv, char **, envp); 2869 str = "execve";
2868 PRE_MEM_RASCIIZ( "execve(filename)", ARG1 ); 2870
2869 if (ARG2 != 0) { 2871 VG_(strcpy)(str2, str);
2870 /* At least the terminating NULL must be addressable. */ 2872 VG_(strcpy)(str3, str);
2871 if (!ML_(safe_to_deref)((HChar **) (Addr)ARG2, sizeof(HChar *))) { 2873
2874 if (arg_2 != 0) {
2875 /* At least the terminating NULL must be addressable. */
2876 if (!ML_(safe_to_deref)((HChar **) (Addr)arg_2, sizeof(HChar *))) {
2872 SET_STATUS_Failure(VKI_EFAULT); 2877 SET_STATUS_Failure(VKI_EFAULT);
2873 return; 2878 return;
2874 } 2879 }
2875 ML_(pre_argv_envp)( ARG2, tid, "execve(argv)", "execve(argv[i])" ); 2880 VG_(strcat)(str2, "(argv)");
2881 VG_(strcat)(str3, "(argv[i])");
2882 ML_(pre_argv_envp)( arg_2, tid, str2, str3 );
2876 } 2883 }
2877 if (ARG3 != 0) { 2884 // Reset helper strings to syscall name.
2885 str2[VG_(strlen)(str)] = '\0';
2886 str3[VG_(strlen)(str)] = '\0';
2887 if (arg_3 != 0) {
2878 /* At least the terminating NULL must be addressable. */ 2888 /* At least the terminating NULL must be addressable. */
2879 if (!ML_(safe_to_deref)((HChar **) (Addr)ARG3, sizeof(HChar *))) { 2889 if (!ML_(safe_to_deref)((HChar **) (Addr)arg_3, sizeof(HChar *))) {
2880 SET_STATUS_Failure(VKI_EFAULT); 2890 SET_STATUS_Failure(VKI_EFAULT);
2881 return; 2891 return;
2882 } 2892 }
2883 ML_(pre_argv_envp)( ARG3, tid, "execve(envp)", "execve(envp[i])" ); 2893 VG_(strcat)(str2, "(envp)");
2894 VG_(strcat)(str3, "(envp[i])");
2895 ML_(pre_argv_envp)( arg_3, tid, str2, str3 );
2884 } 2896 }
2885 2897
2886 vg_assert(VG_(is_valid_tid)(tid)); 2898 vg_assert(VG_(is_valid_tid)(tid));
@@ -2893,35 +2905,36 @@ PRE(sys_execve)
2893 an effort to check that the execve will work before actually 2905 an effort to check that the execve will work before actually
2894 doing it. */ 2906 doing it. */
2895 2907
2896 /* Check that the name at least begins in client-accessible storage. */ 2908 /* Check that the name at least begins in client-accessible storage.
2897 if (ARG1 == 0 /* obviously bogus */ 2909 If we didn't create it ourselves in execveat. */
2898 || !VG_(am_is_valid_for_client)( ARG1, 1, VKI_PROT_READ )) { 2910 if (check_pathptr
2899 SET_STATUS_Failure( VKI_EFAULT ); 2911 && !VG_(am_is_valid_for_client)( pathname, 1, VKI_PROT_READ )) {
2900 return; 2912 SET_STATUS_Failure( VKI_EFAULT );
2913 return;
2901 } 2914 }
2902 2915
2903 // debug-only printing 2916 // debug-only printing
2904 if (0) { 2917 if (0) {
2905 VG_(printf)("ARG1 = %p(%s)\n", (void*)(Addr)ARG1, (HChar*)(Addr)ARG1); 2918 VG_(printf)("pathname = %p(%s)\n", (void*)(Addr)pathname, (HChar*)(Addr)pathname);
2906 if (ARG2) { 2919 if (arg_2) {
2907 VG_(printf)("ARG2 = "); 2920 VG_(printf)("arg_2 = ");
2908 Int q; 2921 Int q;
2909 HChar** vec = (HChar**)(Addr)ARG2; 2922 HChar** vec = (HChar**)(Addr)arg_2;
2910 for (q = 0; vec[q]; q++) 2923 for (q = 0; vec[q]; q++)
2911 VG_(printf)("%p(%s) ", vec[q], vec[q]); 2924 VG_(printf)("%p(%s) ", vec[q], vec[q]);
2912 VG_(printf)("\n"); 2925 VG_(printf)("\n");
2913 } else { 2926 } else {
2914 VG_(printf)("ARG2 = null\n"); 2927 VG_(printf)("arg_2 = null\n");
2915 } 2928 }
2916 } 2929 }
2917 2930
2918 // Decide whether or not we want to follow along 2931 // Decide whether or not we want to follow along
2919 { // Make 'child_argv' be a pointer to the child's arg vector 2932 { // Make 'child_argv' be a pointer to the child's arg vector
2920 // (skipping the exe name) 2933 // (skipping the exe name)
2921 const HChar** child_argv = (const HChar**)(Addr)ARG2; 2934 const HChar** child_argv = (const HChar**)(Addr)arg_2;
2922 if (child_argv && child_argv[0] == NULL) 2935 if (child_argv && child_argv[0] == NULL)
2923 child_argv = NULL; 2936 child_argv = NULL;
2924 trace_this_child = VG_(should_we_trace_this_child)( (HChar*)(Addr)ARG1, 2937 trace_this_child = VG_(should_we_trace_this_child)( (HChar*)(Addr)pathname,
2925 child_argv ); 2938 child_argv );
2926 } 2939 }
2927 2940
@@ -2929,7 +2942,7 @@ PRE(sys_execve)
2929 // ok, etc. We allow setuid executables to run only in the case when 2942 // ok, etc. We allow setuid executables to run only in the case when
2930 // we are not simulating them, that is, they to be run natively. 2943 // we are not simulating them, that is, they to be run natively.
2931 setuid_allowed = trace_this_child ? False : True; 2944 setuid_allowed = trace_this_child ? False : True;
2932 res = VG_(pre_exec_check)((const HChar *)(Addr)ARG1, NULL, setuid_allowed); 2945 res = VG_(pre_exec_check)((const HChar *)(Addr)pathname, NULL, setuid_allowed);
2933 if (sr_isError(res)) { 2946 if (sr_isError(res)) {
2934 SET_STATUS_Failure( sr_Err(res) ); 2947 SET_STATUS_Failure( sr_Err(res) );
2935 return; 2948 return;
@@ -2946,7 +2959,7 @@ PRE(sys_execve)
2946 } 2959 }
2947 2960
2948 /* After this point, we can't recover if the execve fails. */ 2961 /* After this point, we can't recover if the execve fails. */
2949 VG_(debugLog)(1, "syswrap", "Exec of %s\n", (HChar*)(Addr)ARG1); 2962 VG_(debugLog)(1, "syswrap", "Exec of %s\n", (HChar*)(Addr)pathname);
2950 2963
2951 2964
2952 // Terminate gdbserver if it is active. 2965 // Terminate gdbserver if it is active.
@@ -2982,7 +2995,7 @@ PRE(sys_execve)
2982 } 2995 }
2983 2996
2984 } else { 2997 } else {
2985 path = (HChar*)(Addr)ARG1; 2998 path = (HChar*)(Addr)pathname;
2986 } 2999 }
2987 3000
2988 // Set up the child's environment. 3001 // Set up the child's environment.
@@ -2996,29 +3009,29 @@ PRE(sys_execve)
2996 // 3009 //
2997 // Then, if tracing the child, set VALGRIND_LIB for it. 3010 // Then, if tracing the child, set VALGRIND_LIB for it.
2998 // 3011 //
2999 if (ARG3 == 0) { 3012 if (arg_3 == 0) {
3000 envp = NULL; 3013 envp = NULL;
3001 } else { 3014 } else {
3002 envp = VG_(env_clone)( (HChar**)(Addr)ARG3 ); 3015 envp = VG_(env_clone)( (HChar**)(Addr)arg_3 );
3003 if (envp == NULL) goto hosed; 3016 if (envp == NULL) goto hosed;
3004 VG_(env_remove_valgrind_env_stuff)( envp, True /*ro_strings*/, NULL ); 3017 VG_(env_remove_valgrind_env_stuff)( envp, True /*ro_strings*/, NULL );
3005 } 3018 }
3006 3019
3007 if (trace_this_child) { 3020 if (trace_this_child) {
3008 // Set VALGRIND_LIB in ARG3 (the environment) 3021 // Set VALGRIND_LIB in arg_3 (the environment)
3009 VG_(env_setenv)( &envp, VALGRIND_LIB, VG_(libdir)); 3022 VG_(env_setenv)( &envp, VALGRIND_LIB, VG_(libdir));
3010 } 3023 }
3011 3024
3012 // Set up the child's args. If not tracing it, they are 3025 // Set up the child's args. If not tracing it, they are
3013 // simply ARG2. Otherwise, they are 3026 // simply arg_2. Otherwise, they are
3014 // 3027 //
3015 // [launcher_basename] ++ VG_(args_for_valgrind) ++ [ARG1] ++ ARG2[1..] 3028 // [launcher_basename] ++ VG_(args_for_valgrind) ++ [pathname] ++ arg_2[1..]
3016 // 3029 //
3017 // except that the first VG_(args_for_valgrind_noexecpass) args 3030 // except that the first VG_(args_for_valgrind_noexecpass) args
3018 // are omitted. 3031 // are omitted.
3019 // 3032 //
3020 if (!trace_this_child) { 3033 if (!trace_this_child) {
3021 argv = (HChar**)(Addr)ARG2; 3034 argv = (HChar**)(Addr)arg_2;
3022 } else { 3035 } else {
3023 vg_assert( VG_(args_for_valgrind) ); 3036 vg_assert( VG_(args_for_valgrind) );
3024 vg_assert( VG_(args_for_valgrind_noexecpass) >= 0 ); 3037 vg_assert( VG_(args_for_valgrind_noexecpass) >= 0 );
@@ -3033,7 +3046,7 @@ PRE(sys_execve)
3033 // name of client exe 3046 // name of client exe
3034 tot_args++; 3047 tot_args++;
3035 // args for client exe, skipping [0] 3048 // args for client exe, skipping [0]
3036 arg2copy = (HChar**)(Addr)ARG2; 3049 arg2copy = (HChar**)(Addr)arg_2;
3037 if (arg2copy && arg2copy[0]) { 3050 if (arg2copy && arg2copy[0]) {
3038 for (i = 1; arg2copy[i]; i++) 3051 for (i = 1; arg2copy[i]; i++)
3039 tot_args++; 3052 tot_args++;
@@ -3049,7 +3062,7 @@ PRE(sys_execve)
3049 continue; 3062 continue;
3050 argv[j++] = * (HChar**) VG_(indexXA)( VG_(args_for_valgrind), i ); 3063 argv[j++] = * (HChar**) VG_(indexXA)( VG_(args_for_valgrind), i );
3051 } 3064 }
3052 argv[j++] = (HChar*)(Addr)ARG1; 3065 argv[j++] = (HChar*)(Addr)pathname;
3053 if (arg2copy && arg2copy[0]) 3066 if (arg2copy && arg2copy[0])
3054 for (i = 1; arg2copy[i]; i++) 3067 for (i = 1; arg2copy[i]; i++)
3055 argv[j++] = arg2copy[i]; 3068 argv[j++] = arg2copy[i];
@@ -3113,9 +3126,9 @@ PRE(sys_execve)
3113 VG_(printf)("env: %s\n", *cpp); 3126 VG_(printf)("env: %s\n", *cpp);
3114 } 3127 }
3115 3128
3129 // always execute this because it's executing valgrind, not the "target" exe
3116 SET_STATUS_from_SysRes( 3130 SET_STATUS_from_SysRes(
3117 VG_(do_syscall3)(__NR_execve, (UWord)path, (UWord)argv, (UWord)envp) 3131 VG_(do_syscall3)(__NR_execve, (UWord)path, (UWord)argv, (UWord)envp));
3118 );
3119 3132
3120 /* If we got here, then the execve failed. We've already made way 3133 /* If we got here, then the execve failed. We've already made way
3121 too much of a mess to continue, so we have to abort. */ 3134 too much of a mess to continue, so we have to abort. */
@@ -3123,12 +3136,30 @@ PRE(sys_execve)
3123 vg_assert(FAILURE); 3136 vg_assert(FAILURE);
3124 VG_(message)(Vg_UserMsg, "execve(%#" FMT_REGWORD "x(%s), %#" FMT_REGWORD 3137 VG_(message)(Vg_UserMsg, "execve(%#" FMT_REGWORD "x(%s), %#" FMT_REGWORD
3125 "x, %#" FMT_REGWORD "x) failed, errno %lu\n", 3138 "x, %#" FMT_REGWORD "x) failed, errno %lu\n",
3126 ARG1, (HChar*)(Addr)ARG1, ARG2, ARG3, ERR); 3139 pathname, (HChar*)(Addr)pathname, arg_2, arg_3, ERR);
3127 VG_(message)(Vg_UserMsg, "EXEC FAILED: I can't recover from " 3140 VG_(message)(Vg_UserMsg, "EXEC FAILED: I can't recover from "
3128 "execve() failing, so I'm dying.\n"); 3141 "execve() failing, so I'm dying.\n");
3129 VG_(message)(Vg_UserMsg, "Add more stringent tests in PRE(sys_execve), " 3142 VG_(message)(Vg_UserMsg, "Add more stringent tests in PRE(sys_execve), "
3130 "or work out how to recover.\n"); 3143 "or work out how to recover.\n");
3131 VG_(exit)(101); 3144 VG_(exit)(101);
3145
3146}
3147
3148// XXX: prototype here seemingly doesn't match the prototype for i386-linux,
3149// but it seems to work nonetheless...
3150PRE(sys_execve)
3151{
3152 PRINT("sys_execve ( %#" FMT_REGWORD "x(%s), %#" FMT_REGWORD "x, %#"
3153 FMT_REGWORD "x )", ARG1, (HChar*)(Addr)ARG1, ARG2, ARG3);
3154 PRE_REG_READ3(vki_off_t, "execve",
3155 char *, filename, char **, argv, char **, envp);
3156 PRE_MEM_RASCIIZ( "execve(filename)", ARG1 );
3157
3158 char *pathname = (char *)ARG1;
3159 Addr arg_2 = (Addr)ARG2;
3160 Addr arg_3 = (Addr)ARG3;
3161
3162 handle_pre_sys_execve(tid, status, (Addr)pathname, arg_2, arg_3, 0, True);
3132} 3163}
3133 3164
3134PRE(sys_access) 3165PRE(sys_access)
diff --git a/coregrind/m_syswrap/syswrap-linux.c b/coregrind/m_syswrap/syswrap-linux.c
index b32bd21..5b5b7ee 100644
--- a/coregrind/m_syswrap/syswrap-linux.c
+++ b/coregrind/m_syswrap/syswrap-linux.c
@@ -13138,6 +13138,87 @@ POST(sys_io_uring_register)
13138{ 13138{
13139} 13139}
13140 13140
13141PRE(sys_execveat)
13142{
13143 PRINT("sys_execveat ( %lu, %#lx(%s), %#lx, %#lx, %lu", ARG1, ARG2, (char*)ARG2, ARG3, ARG4, ARG5);
13144 PRE_REG_READ5(vki_off_t, "execveat",
13145 int, fd, char *, filename, char **, argv, char **, envp, int, flags);
13146 PRE_MEM_RASCIIZ( "execveat(filename)", ARG2);
13147
13148#if !defined(__NR_execveat)
13149 SET_STATUS_Failure(VKI_ENOSYS);
13150 return;
13151#endif
13152
13153 char *path = (char*) ARG2;
13154 Addr arg_2 = ARG3;
13155 Addr arg_3 = ARG4;
13156 const HChar *buf;
13157 HChar *abs_path = NULL;
13158 Bool check_at_symlink = False;
13159 Bool check_pathptr = True;
13160
13161 if (ML_(safe_to_deref) (path, 1)) {
13162 /* If pathname is absolute, we'll ignore dirfd
13163 * and just pass the pathname, try to determine
13164 * the absolute path otherwise. */
13165 if (path[0] != '/') {
13166 /* Check dirfd is a valid fd. */
13167 if (!ML_(fd_allowed)(ARG1, "execveat", tid, False)) {
13168 SET_STATUS_Failure( VKI_EBADF );
13169 return;
13170 }
13171 /* If pathname is empty and AT_EMPTY_PATH is
13172 set then dirfd describes the whole path. */
13173 if (path[0] == '\0') {
13174 if (ARG5 & VKI_AT_EMPTY_PATH) {
13175 if (VG_(resolve_filename)(ARG1, &buf)) {
13176 VG_(strcpy)(path, buf);
13177 check_pathptr = False;
13178 }
13179 }
13180 }
13181 else if (ARG1 == VKI_AT_FDCWD) {
13182 check_at_symlink = True;
13183 } else
13184 if (ARG5 & VKI_AT_SYMLINK_NOFOLLOW)
13185 check_at_symlink = True;
13186 else if (VG_(resolve_filename)(ARG1, &buf)) {
13187 abs_path = VG_(malloc)("execveat",
13188 (VG_(strlen)(buf) + 1
13189 + VG_(strlen)(path) + 1));
13190 VG_(sprintf)(abs_path, "%s/%s", buf, path);
13191 path = abs_path;
13192 check_pathptr = False;
13193 }
13194 else
13195 path = NULL;
13196 if (check_at_symlink) {
13197 struct vg_stat statbuf;
13198 SysRes statres;
13199
13200 statres = VG_(stat)(path, &statbuf);
13201 if (sr_isError(statres) || VKI_S_ISLNK(statbuf.mode)) {
13202 SET_STATUS_Failure( VKI_ELOOP );
13203 return;
13204 }
13205 }
13206 }
13207 } else {
13208 SET_STATUS_Failure(VKI_EFAULT);
13209 return;
13210 }
13211
13212 handle_pre_sys_execve(tid, status, (Addr) path, arg_2, arg_3, 1,
13213 check_pathptr);
13214
13215 /* The exec failed, we keep running... cleanup. */
13216 VG_(free)(abs_path);
13217
13218
13219}
13220
13221
13141#undef PRE 13222#undef PRE
13142#undef POST 13223#undef POST
13143 13224
diff --git a/include/vki/vki-linux.h b/include/vki/vki-linux.h
index ffda18f..75b5831 100644
--- a/include/vki/vki-linux.h
+++ b/include/vki/vki-linux.h
@@ -1296,6 +1296,8 @@ struct vki_seminfo {
1296 1296
1297#define VKI_EWOULDBLOCK VKI_EAGAIN 1297#define VKI_EWOULDBLOCK VKI_EAGAIN
1298 1298
1299#define VKI_ELOOP 40
1300
1299//---------------------------------------------------------------------- 1301//----------------------------------------------------------------------
1300// From linux-2.6.8.1/include/linux/wait.h 1302// From linux-2.6.8.1/include/linux/wait.h
1301//---------------------------------------------------------------------- 1303//----------------------------------------------------------------------
@@ -1502,6 +1504,7 @@ struct vki_flock64 {
1502}; 1504};
1503 1505
1504#define VKI_AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */ 1506#define VKI_AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */
1507#define VKI_AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */
1505 1508
1506//---------------------------------------------------------------------- 1509//----------------------------------------------------------------------
1507// From linux-2.6.8.1/include/linux/sysctl.h 1510// From linux-2.6.8.1/include/linux/sysctl.h
diff --git a/memcheck/tests/linux/Makefile.am b/memcheck/tests/linux/Makefile.am
index 14d4a07..3111f63 100644
--- a/memcheck/tests/linux/Makefile.am
+++ b/memcheck/tests/linux/Makefile.am
@@ -28,7 +28,8 @@ EXTRA_DIST = \
28 proc-auxv.vgtest proc-auxv.stderr.exp getregset.vgtest \ 28 proc-auxv.vgtest proc-auxv.stderr.exp getregset.vgtest \
29 getregset.stderr.exp getregset.stdout.exp \ 29 getregset.stderr.exp getregset.stdout.exp \
30 sys-preadv_pwritev.vgtest sys-preadv_pwritev.stderr.exp \ 30 sys-preadv_pwritev.vgtest sys-preadv_pwritev.stderr.exp \
31 sys-preadv2_pwritev2.vgtest sys-preadv2_pwritev2.stderr.exp 31 sys-preadv2_pwritev2.vgtest sys-preadv2_pwritev2.stderr.exp \
32 sys-execveat.vgtest sys-execveat.stderr.exp sys-execveat.stdout.exp
32 33
33check_PROGRAMS = \ 34check_PROGRAMS = \
34 brk \ 35 brk \
@@ -47,7 +48,9 @@ check_PROGRAMS = \
47 syslog-syscall \ 48 syslog-syscall \
48 sys-statx \ 49 sys-statx \
49 timerfd-syscall \ 50 timerfd-syscall \
50 proc-auxv 51 proc-auxv \
52 sys-execveat \
53 check_execveat
51 54
52if HAVE_AT_FDCWD 55if HAVE_AT_FDCWD
53check_PROGRAMS += sys-openat 56check_PROGRAMS += sys-openat
diff --git a/memcheck/tests/linux/check_execveat.c b/memcheck/tests/linux/check_execveat.c
new file mode 100644
index 0000000..df718b6
--- /dev/null
+++ b/memcheck/tests/linux/check_execveat.c
@@ -0,0 +1,18 @@
1#include <sys/syscall.h>
2#include <errno.h>
3#include <unistd.h>
4#include <stddef.h>
5
6int main(int argc, char **argv)
7{
8 int has_execveat = 0;
9#if defined(__NR_execveat)
10 errno = 0;
11 syscall(__NR_execveat, 0, NULL, 0, 0, 0);
12 has_execveat = (errno != ENOSYS);
13#else
14 has_execveat = 0;
15#endif
16
17 return has_execveat ? 0 : -1;
18}
diff --git a/memcheck/tests/linux/sys-execveat.c b/memcheck/tests/linux/sys-execveat.c
new file mode 100644
index 0000000..921b888
--- /dev/null
+++ b/memcheck/tests/linux/sys-execveat.c
@@ -0,0 +1,64 @@
1#include <sys/syscall.h>
2#include <errno.h>
3#include <dirent.h>
4#include <unistd.h>
5#include <stddef.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <sys/stat.h>
9#include <fcntl.h>
10
11static int sys_execveat (int dirfd, const char *pathname,
12 char *const argv[], char *const envp[],
13 int flags)
14{
15#if defined(__NR_execveat)
16 return syscall(__NR_execveat, dirfd, pathname, argv, envp, flags);
17#else
18 errno = ENOSYS;
19 return -1;
20#endif
21}
22
23
24int main()
25{
26 char *argv[] = { "foobar", "execveat exists", NULL };
27 char *envp[] = { NULL };
28 DIR *dirp;
29 int fd;
30
31 dirp = opendir("/bin");
32 if (dirp == NULL) {
33 perror("execveat");
34 exit(EXIT_FAILURE);
35 }
36 fd = dirfd(dirp);
37
38 /* Check valgrind will produce expected warnings for the
39 various wrong arguments. */
40 do {
41 char *mem = malloc(16);
42 void *t = (void *) &mem[0];
43 void *z = (void *) -1;
44 int flag = *((int *) &mem[8]);
45
46 sys_execveat(-1, "bin/xecho", argv, envp, 0);
47 sys_execveat(-1, "xecho", argv, envp, 0);
48 sys_execveat(fd, "xecho", argv, envp, flag);
49 sys_execveat(fd, "", argv, envp, 0);
50 sys_execveat(fd, NULL, argv, envp, 0);
51 sys_execveat(fd, "xecho", t, envp, 0);
52 sys_execveat(fd, "xecho", z, envp, 0);
53 } while (0);
54
55 /* Check execveat called with the correct arguments works. */
56 if (sys_execveat(fd, "echo", argv, envp, 0) == -1) {
57 perror("execveat");
58 exit(EXIT_FAILURE);
59 }
60
61 closedir(dirp);
62 exit(EXIT_SUCCESS);
63}
64
diff --git a/memcheck/tests/linux/sys-execveat.stderr.exp b/memcheck/tests/linux/sys-execveat.stderr.exp
new file mode 100644
index 0000000..a58b0fb
--- /dev/null
+++ b/memcheck/tests/linux/sys-execveat.stderr.exp
@@ -0,0 +1,19 @@
1Syscall param execveat(flags) contains uninitialised byte(s)
2 ...
3 by 0x........: sys_execveat (sys-execveat.c:16)
4 by 0x........: main (sys-execveat.c:48)
5
6Syscall param execveat(filename) points to unaddressable byte(s)
7 ...
8 by 0x........: sys_execveat (sys-execveat.c:16)
9 by 0x........: main (sys-execveat.c:50)
10 Address 0x........ is not stack'd, malloc'd or (recently) free'd
11
12Syscall param execveat(argv) points to uninitialised byte(s)
13 ...
14 by 0x........: sys_execveat (sys-execveat.c:16)
15 by 0x........: main (sys-execveat.c:51)
16 Address 0x........ is 0 bytes inside a block of size 16 alloc'd
17 at 0x........: malloc (vg_replace_malloc.c:...)
18 by 0x........: main (sys-execveat.c:41)
19
diff --git a/memcheck/tests/linux/sys-execveat.stdout.exp b/memcheck/tests/linux/sys-execveat.stdout.exp
new file mode 100644
index 0000000..c7e51ee
--- /dev/null
+++ b/memcheck/tests/linux/sys-execveat.stdout.exp
@@ -0,0 +1 @@
execveat exists
diff --git a/memcheck/tests/linux/sys-execveat.vgtest b/memcheck/tests/linux/sys-execveat.vgtest
new file mode 100644
index 0000000..87ac016
--- /dev/null
+++ b/memcheck/tests/linux/sys-execveat.vgtest
@@ -0,0 +1,3 @@
1prereq: ./check_execveat
2prog: sys-execveat
3vgopts: -q