1616#include <string.h>
1717#include <fcntl.h>
1818#include <limits.h>
19+ #include <signal.h>
1920#include <sysexits.h>
2021
22+ #include <sys/ptrace.h>
23+ #include <sys/user.h>
24+ #include <sys/wait.h>
25+ #include <sys/mman.h>
26+
27+ #include <linux/ptrace.h>
28+
2129/* Common system call numbers */
2230#define SYS_READ 0
2331#define SYS_WRITE 1
3341
3442#define X32_BIT 0x40000000
3543
36- static unsigned int nerr = 0 ; /* Cumulative error count */
3744static int nullfd = -1 ; /* File descriptor for /dev/null */
38- static int indent = 0 ;
45+ static bool with_x32 ; /* x32 supported on this kernel? */
46+
47+ enum ptrace_pass {
48+ PTP_NOTHING ,
49+ PTP_GETREGS ,
50+ PTP_WRITEBACK ,
51+ PTP_FUZZRET ,
52+ PTP_FUZZHIGH ,
53+ PTP_INTNUM ,
54+ PTP_DONE
55+ };
56+
57+ static const char * const ptrace_pass_name [] =
58+ {
59+ [PTP_NOTHING ] = "just stop, no data read" ,
60+ [PTP_GETREGS ] = "only getregs" ,
61+ [PTP_WRITEBACK ] = "getregs, unmodified setregs" ,
62+ [PTP_FUZZRET ] = "modifying the default return" ,
63+ [PTP_FUZZHIGH ] = "clobbering the top 32 bits" ,
64+ [PTP_INTNUM ] = "sign-extending the syscall number" ,
65+ };
66+
67+ /*
68+ * Shared memory block between tracer and test
69+ */
70+ struct shared {
71+ unsigned int nerr ; /* Total error count */
72+ unsigned int indent ; /* Message indentation level */
73+ enum ptrace_pass ptrace_pass ;
74+ bool probing_syscall ; /* In probe_syscall() */
75+ };
76+ static volatile struct shared * sh ;
3977
4078static inline unsigned int offset (void )
4179{
42- return 8 + indent * 4 ;
80+ unsigned int level = sh ? sh -> indent : 0 ;
81+
82+ return 8 + level * 4 ;
4383}
4484
4585#define msg (lvl , fmt , ...) printf("%-*s" fmt, offset(), "[" #lvl "]", \
@@ -52,24 +92,27 @@ static inline unsigned int offset(void)
5292#define fail (fmt , ...) \
5393 do { \
5494 msg(FAIL, fmt, ## __VA_ARGS__); \
55- nerr++; \
56- } while (0)
95+ sh-> nerr++; \
96+ } while (0)
5797
5898#define crit (fmt , ...) \
5999 do { \
60- indent = 0; \
100+ sh-> indent = 0; \
61101 msg(FAIL, fmt, ## __VA_ARGS__); \
62102 msg(SKIP, "Unable to run test\n"); \
63- exit(EX_OSERR);
64- } while (0 )
103+ exit(EX_OSERR); \
104+ } while (0)
105+
106+ /* Sentinel for ptrace-modified return value */
107+ #define MODIFIED_BY_PTRACE -9999
65108
66109/*
67110 * Directly invokes the given syscall with nullfd as the first argument
68111 * and the rest zero. Avoids involving glibc wrappers in case they ever
69112 * end up intercepting some system calls for some reason, or modify
70113 * the system call number itself.
71114 */
72- static inline long long probe_syscall (int msb , int lsb )
115+ static long long probe_syscall (int msb , int lsb )
73116{
74117 register long long arg1 asm("rdi" ) = nullfd ;
75118 register long long arg2 asm("rsi" ) = 0 ;
@@ -80,11 +123,21 @@ static inline long long probe_syscall(int msb, int lsb)
80123 long long nr = ((long long )msb << 32 ) | (unsigned int )lsb ;
81124 long long ret ;
82125
126+ /*
127+ * We pass in an extra copy of the extended system call number
128+ * in %rbx, so we can examine it from the ptrace handler without
129+ * worrying about it being possibly modified. This is to test
130+ * the validity of struct user regs.orig_rax a.k.a.
131+ * struct pt_regs.orig_ax.
132+ */
133+ sh -> probing_syscall = true;
83134 asm volatile ("syscall"
84135 : "=a" (ret )
85- : "a" (nr ), "r" (arg1 ), "r" (arg2 ), "r" (arg3 ),
136+ : "a" (nr ), "b" (nr ),
137+ "r" (arg1 ), "r" (arg2 ), "r" (arg3 ),
86138 "r" (arg4 ), "r" (arg5 ), "r" (arg6 )
87139 : "rcx" , "r11" , "memory" , "cc" );
140+ sh -> probing_syscall = false;
88141
89142 return ret ;
90143}
@@ -119,9 +172,9 @@ static unsigned int _check_for(int msb, int start, int end, long long expect,
119172{
120173 unsigned int err = 0 ;
121174
122- indent ++ ;
175+ sh -> indent ++ ;
123176 if (start != end )
124- indent ++ ;
177+ sh -> indent ++ ;
125178
126179 for (int nr = start ; nr <= end ; nr ++ ) {
127180 long long ret = probe_syscall (msb , nr );
@@ -135,20 +188,19 @@ static unsigned int _check_for(int msb, int start, int end, long long expect,
135188 }
136189
137190 if (start != end )
138- indent -- ;
191+ sh -> indent -- ;
139192
140193 if (err ) {
141- nerr += err ;
142194 if (start != end )
143195 fail ("%s had %u failure%s\n" ,
144- syscall_str (msb , start , end ),
145- err , err == 1 ? "s" : "" );
196+ syscall_str (msb , start , end ),
197+ err , err == 1 ? "s" : "" );
146198 } else {
147199 ok ("%s returned %s as expected\n" ,
148200 syscall_str (msb , start , end ), expect_str );
149201 }
150202
151- indent -- ;
203+ sh -> indent -- ;
152204
153205 return err ;
154206}
@@ -175,28 +227,29 @@ static bool test_x32(void)
175227{
176228 long long ret ;
177229 pid_t mypid = getpid ();
178- bool with_x32 ;
179230
180231 run ("Checking for x32 by calling x32 getpid()\n" );
181232 ret = probe_syscall (0 , SYS_GETPID | X32_BIT );
182233
183- indent ++ ;
234+ sh -> indent ++ ;
184235 if (ret == mypid ) {
185236 info ("x32 is supported\n" );
186237 with_x32 = true;
187238 } else if (ret == - ENOSYS ) {
188239 info ("x32 is not supported\n" );
189240 with_x32 = false;
190241 } else {
191- fail ("x32 getpid() returned %lld, but it should have returned either %lld or -ENOSYS\n" , ret , mypid );
242+ fail ("x32 getpid() returned %lld, but it should have returned either %lld or -ENOSYS\n" , ret , ( long long ) mypid );
192243 with_x32 = false;
193244 }
194- indent -- ;
245+ sh -> indent -- ;
195246 return with_x32 ;
196247}
197248
198249static void test_syscalls_common (int msb )
199250{
251+ enum ptrace_pass pass = sh -> ptrace_pass ;
252+
200253 run ("Checking some common syscalls as 64 bit\n" );
201254 check_zero (msb , SYS_READ );
202255 check_zero (msb , SYS_WRITE );
@@ -206,7 +259,11 @@ static void test_syscalls_common(int msb)
206259 check_zero (msb , X64_WRITEV );
207260
208261 run ("Checking out of range system calls\n" );
209- check_for (msb , -64 , -1 , - ENOSYS );
262+ check_for (msb , -64 , -2 , - ENOSYS );
263+ if (pass >= PTP_FUZZRET )
264+ check_for (msb , -1 , -1 , MODIFIED_BY_PTRACE );
265+ else
266+ check_for (msb , -1 , -1 , - ENOSYS );
210267 check_for (msb , X32_BIT - 64 , X32_BIT - 1 , - ENOSYS );
211268 check_for (msb , -64 - X32_BIT , -1 - X32_BIT , - ENOSYS );
212269 check_for (msb , INT_MAX - 64 , INT_MAX - 1 , - ENOSYS );
@@ -249,7 +306,8 @@ static void test_syscall_numbering(void)
249306 0 , 1 , -1 , X32_BIT - 1 , X32_BIT , X32_BIT - 1 , - X32_BIT , INT_MAX ,
250307 INT_MIN , INT_MIN + 1
251308 };
252- bool with_x32 = test_x32 ();
309+
310+ sh -> indent ++ ;
253311
254312 /*
255313 * The MSB is supposed to be ignored, so we loop over a few
@@ -260,20 +318,127 @@ static void test_syscall_numbering(void)
260318 run ("Checking system calls with msb = %d (0x%x)\n" ,
261319 msb , msb );
262320
263- indent ++ ;
321+ sh -> indent ++ ;
264322
265323 test_syscalls_common (msb );
266324 if (with_x32 )
267325 test_syscalls_with_x32 (msb );
268326 else
269327 test_syscalls_without_x32 (msb );
270328
271- indent -- ;
329+ sh -> indent -- ;
330+ }
331+
332+ sh -> indent -- ;
333+ }
334+
335+ static void syscall_numbering_tracee (void )
336+ {
337+ enum ptrace_pass pass ;
338+
339+ if (ptrace (PTRACE_TRACEME , 0 , 0 , 0 )) {
340+ crit ("Failed to request tracing\n" );
341+ return ;
342+ }
343+ raise (SIGSTOP );
344+
345+ for (sh -> ptrace_pass = pass = PTP_NOTHING ; pass < PTP_DONE ;
346+ sh -> ptrace_pass = ++ pass ) {
347+ run ("Running tests under ptrace: %s\n" , ptrace_pass_name [pass ]);
348+ test_syscall_numbering ();
349+ }
350+ }
351+
352+ static void mess_with_syscall (pid_t testpid , enum ptrace_pass pass )
353+ {
354+ struct user_regs_struct regs ;
355+
356+ sh -> probing_syscall = false; /* Do this on entry only */
357+
358+ /* For these, don't even getregs */
359+ if (pass == PTP_NOTHING || pass == PTP_DONE )
360+ return ;
361+
362+ ptrace (PTRACE_GETREGS , testpid , NULL , & regs );
363+
364+ if (regs .orig_rax != regs .rbx ) {
365+ fail ("orig_rax %#llx doesn't match syscall number %#llx\n" ,
366+ (unsigned long long )regs .orig_rax ,
367+ (unsigned long long )regs .rbx );
368+ }
369+
370+ switch (pass ) {
371+ case PTP_GETREGS :
372+ /* Just read, no writeback */
373+ return ;
374+ case PTP_WRITEBACK :
375+ /* Write back the same register state verbatim */
376+ break ;
377+ case PTP_FUZZRET :
378+ regs .rax = MODIFIED_BY_PTRACE ;
379+ break ;
380+ case PTP_FUZZHIGH :
381+ regs .rax = MODIFIED_BY_PTRACE ;
382+ regs .orig_rax = regs .orig_rax | 0xffffffff00000000ULL ;
383+ break ;
384+ case PTP_INTNUM :
385+ regs .rax = MODIFIED_BY_PTRACE ;
386+ regs .orig_rax = (int )regs .orig_rax ;
387+ break ;
388+ default :
389+ crit ("invalid ptrace_pass\n" );
390+ break ;
391+ }
392+
393+ ptrace (PTRACE_SETREGS , testpid , NULL , & regs );
394+ }
395+
396+ static void syscall_numbering_tracer (pid_t testpid )
397+ {
398+ int wstatus ;
399+
400+ do {
401+ pid_t wpid = waitpid (testpid , & wstatus , 0 );
402+ if (wpid < 0 && errno != EINTR )
403+ break ;
404+ if (wpid != testpid )
405+ continue ;
406+ if (!WIFSTOPPED (wstatus ))
407+ break ; /* Thread exited? */
408+
409+ if (sh -> probing_syscall && WSTOPSIG (wstatus ) == SIGTRAP )
410+ mess_with_syscall (testpid , sh -> ptrace_pass );
411+ } while (sh -> ptrace_pass != PTP_DONE &&
412+ !ptrace (PTRACE_SYSCALL , testpid , NULL , NULL ));
413+
414+ ptrace (PTRACE_DETACH , testpid , NULL , NULL );
415+
416+ /* Wait for the child process to terminate */
417+ while (waitpid (testpid , & wstatus , 0 ) != testpid || !WIFEXITED (wstatus ))
418+ /* wait some more */ ;
419+ }
420+
421+ static void test_traced_syscall_numbering (void )
422+ {
423+ pid_t testpid ;
424+
425+ /* Launch the test thread; this thread continues as the tracer thread */
426+ testpid = fork ();
427+
428+ if (testpid < 0 ) {
429+ crit ("Unable to launch tracer process\n" );
430+ } else if (testpid == 0 ) {
431+ syscall_numbering_tracee ();
432+ _exit (0 );
433+ } else {
434+ syscall_numbering_tracer (testpid );
272435 }
273436}
274437
275438int main (void )
276439{
440+ unsigned int nerr ;
441+
277442 /*
278443 * It is quite likely to get a segfault on a failure, so make
279444 * sure the message gets out by setting stdout to nonbuffered.
@@ -288,7 +453,24 @@ int main(void)
288453 crit ("Unable to open /dev/null: %s\n" , strerror (errno ));
289454 }
290455
456+ /*
457+ * Set up a block of shared memory...
458+ */
459+ sh = mmap (NULL , sysconf (_SC_PAGE_SIZE ), PROT_READ |PROT_WRITE ,
460+ MAP_ANONYMOUS |MAP_SHARED , 0 , 0 );
461+ if (sh == MAP_FAILED ) {
462+ crit ("Unable to allocated shared memory block: %s\n" ,
463+ strerror (errno ));
464+ }
465+
466+ with_x32 = test_x32 ();
467+
468+ run ("Running tests without ptrace...\n" );
291469 test_syscall_numbering ();
470+
471+ test_traced_syscall_numbering ();
472+
473+ nerr = sh -> nerr ;
292474 if (!nerr ) {
293475 ok ("All system calls succeeded or failed as expected\n" );
294476 return 0 ;
0 commit comments