Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/x86/syscall: Add tests under ptrace to syscall_numbering_64

Add tests running under ptrace for syscall_numbering_64. ptrace stopping on
syscall entry and possibly modifying the syscall number (regs.orig_rax) or
the default return value (regs.rax) can have different results than the
normal system call path.

Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20210518191303.4135296-4-hpa@zytor.com

authored by

H. Peter Anvin (Intel) and committed by
Thomas Gleixner
795e2a02 c5c39488

+207 -25
+207 -25
tools/testing/selftests/x86/syscall_numbering.c
··· 16 16 #include <string.h> 17 17 #include <fcntl.h> 18 18 #include <limits.h> 19 + #include <signal.h> 19 20 #include <sysexits.h> 21 + 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> 20 28 21 29 /* Common system call numbers */ 22 30 #define SYS_READ 0 ··· 41 33 42 34 #define X32_BIT 0x40000000 43 35 44 - static unsigned int nerr = 0; /* Cumulative error count */ 45 36 static int nullfd = -1; /* File descriptor for /dev/null */ 46 - static int indent = 0; 37 + static bool with_x32; /* x32 supported on this kernel? */ 38 + 39 + enum ptrace_pass { 40 + PTP_NOTHING, 41 + PTP_GETREGS, 42 + PTP_WRITEBACK, 43 + PTP_FUZZRET, 44 + PTP_FUZZHIGH, 45 + PTP_INTNUM, 46 + PTP_DONE 47 + }; 48 + 49 + static const char * const ptrace_pass_name[] = 50 + { 51 + [PTP_NOTHING] = "just stop, no data read", 52 + [PTP_GETREGS] = "only getregs", 53 + [PTP_WRITEBACK] = "getregs, unmodified setregs", 54 + [PTP_FUZZRET] = "modifying the default return", 55 + [PTP_FUZZHIGH] = "clobbering the top 32 bits", 56 + [PTP_INTNUM] = "sign-extending the syscall number", 57 + }; 58 + 59 + /* 60 + * Shared memory block between tracer and test 61 + */ 62 + struct shared { 63 + unsigned int nerr; /* Total error count */ 64 + unsigned int indent; /* Message indentation level */ 65 + enum ptrace_pass ptrace_pass; 66 + bool probing_syscall; /* In probe_syscall() */ 67 + }; 68 + static volatile struct shared *sh; 47 69 48 70 static inline unsigned int offset(void) 49 71 { 50 - return 8 + indent * 4; 72 + unsigned int level = sh ? sh->indent : 0; 73 + 74 + return 8 + level * 4; 51 75 } 52 76 53 77 #define msg(lvl, fmt, ...) printf("%-*s" fmt, offset(), "[" #lvl "]", \ ··· 92 52 #define fail(fmt, ...) \ 93 53 do { \ 94 54 msg(FAIL, fmt, ## __VA_ARGS__); \ 95 - nerr++; \ 96 - } while (0) 55 + sh->nerr++; \ 56 + } while (0) 97 57 98 58 #define crit(fmt, ...) \ 99 59 do { \ 100 - indent = 0; \ 60 + sh->indent = 0; \ 101 61 msg(FAIL, fmt, ## __VA_ARGS__); \ 102 62 msg(SKIP, "Unable to run test\n"); \ 103 - exit(EX_OSERR); 104 - } while (0) 63 + exit(EX_OSERR); \ 64 + } while (0) 65 + 66 + /* Sentinel for ptrace-modified return value */ 67 + #define MODIFIED_BY_PTRACE -9999 105 68 106 69 /* 107 70 * Directly invokes the given syscall with nullfd as the first argument ··· 112 69 * end up intercepting some system calls for some reason, or modify 113 70 * the system call number itself. 114 71 */ 115 - static inline long long probe_syscall(int msb, int lsb) 72 + static long long probe_syscall(int msb, int lsb) 116 73 { 117 74 register long long arg1 asm("rdi") = nullfd; 118 75 register long long arg2 asm("rsi") = 0; ··· 123 80 long long nr = ((long long)msb << 32) | (unsigned int)lsb; 124 81 long long ret; 125 82 83 + /* 84 + * We pass in an extra copy of the extended system call number 85 + * in %rbx, so we can examine it from the ptrace handler without 86 + * worrying about it being possibly modified. This is to test 87 + * the validity of struct user regs.orig_rax a.k.a. 88 + * struct pt_regs.orig_ax. 89 + */ 90 + sh->probing_syscall = true; 126 91 asm volatile("syscall" 127 92 : "=a" (ret) 128 - : "a" (nr), "r" (arg1), "r" (arg2), "r" (arg3), 93 + : "a" (nr), "b" (nr), 94 + "r" (arg1), "r" (arg2), "r" (arg3), 129 95 "r" (arg4), "r" (arg5), "r" (arg6) 130 96 : "rcx", "r11", "memory", "cc"); 97 + sh->probing_syscall = false; 131 98 132 99 return ret; 133 100 } ··· 172 119 { 173 120 unsigned int err = 0; 174 121 175 - indent++; 122 + sh->indent++; 176 123 if (start != end) 177 - indent++; 124 + sh->indent++; 178 125 179 126 for (int nr = start; nr <= end; nr++) { 180 127 long long ret = probe_syscall(msb, nr); ··· 188 135 } 189 136 190 137 if (start != end) 191 - indent--; 138 + sh->indent--; 192 139 193 140 if (err) { 194 - nerr += err; 195 141 if (start != end) 196 142 fail("%s had %u failure%s\n", 197 - syscall_str(msb, start, end), 198 - err, err == 1 ? "s" : ""); 143 + syscall_str(msb, start, end), 144 + err, err == 1 ? "s" : ""); 199 145 } else { 200 146 ok("%s returned %s as expected\n", 201 147 syscall_str(msb, start, end), expect_str); 202 148 } 203 149 204 - indent--; 150 + sh->indent--; 205 151 206 152 return err; 207 153 } ··· 227 175 { 228 176 long long ret; 229 177 pid_t mypid = getpid(); 230 - bool with_x32; 231 178 232 179 run("Checking for x32 by calling x32 getpid()\n"); 233 180 ret = probe_syscall(0, SYS_GETPID | X32_BIT); 234 181 235 - indent++; 182 + sh->indent++; 236 183 if (ret == mypid) { 237 184 info("x32 is supported\n"); 238 185 with_x32 = true; ··· 239 188 info("x32 is not supported\n"); 240 189 with_x32 = false; 241 190 } else { 242 - fail("x32 getpid() returned %lld, but it should have returned either %lld or -ENOSYS\n", ret, mypid); 191 + fail("x32 getpid() returned %lld, but it should have returned either %lld or -ENOSYS\n", ret, (long long)mypid); 243 192 with_x32 = false; 244 193 } 245 - indent--; 194 + sh->indent--; 246 195 return with_x32; 247 196 } 248 197 249 198 static void test_syscalls_common(int msb) 250 199 { 200 + enum ptrace_pass pass = sh->ptrace_pass; 201 + 251 202 run("Checking some common syscalls as 64 bit\n"); 252 203 check_zero(msb, SYS_READ); 253 204 check_zero(msb, SYS_WRITE); ··· 259 206 check_zero(msb, X64_WRITEV); 260 207 261 208 run("Checking out of range system calls\n"); 262 - check_for(msb, -64, -1, -ENOSYS); 209 + check_for(msb, -64, -2, -ENOSYS); 210 + if (pass >= PTP_FUZZRET) 211 + check_for(msb, -1, -1, MODIFIED_BY_PTRACE); 212 + else 213 + check_for(msb, -1, -1, -ENOSYS); 263 214 check_for(msb, X32_BIT-64, X32_BIT-1, -ENOSYS); 264 215 check_for(msb, -64-X32_BIT, -1-X32_BIT, -ENOSYS); 265 216 check_for(msb, INT_MAX-64, INT_MAX-1, -ENOSYS); ··· 306 249 0, 1, -1, X32_BIT-1, X32_BIT, X32_BIT-1, -X32_BIT, INT_MAX, 307 250 INT_MIN, INT_MIN+1 308 251 }; 309 - bool with_x32 = test_x32(); 252 + 253 + sh->indent++; 310 254 311 255 /* 312 256 * The MSB is supposed to be ignored, so we loop over a few ··· 318 260 run("Checking system calls with msb = %d (0x%x)\n", 319 261 msb, msb); 320 262 321 - indent++; 263 + sh->indent++; 322 264 323 265 test_syscalls_common(msb); 324 266 if (with_x32) ··· 326 268 else 327 269 test_syscalls_without_x32(msb); 328 270 329 - indent--; 271 + sh->indent--; 272 + } 273 + 274 + sh->indent--; 275 + } 276 + 277 + static void syscall_numbering_tracee(void) 278 + { 279 + enum ptrace_pass pass; 280 + 281 + if (ptrace(PTRACE_TRACEME, 0, 0, 0)) { 282 + crit("Failed to request tracing\n"); 283 + return; 284 + } 285 + raise(SIGSTOP); 286 + 287 + for (sh->ptrace_pass = pass = PTP_NOTHING; pass < PTP_DONE; 288 + sh->ptrace_pass = ++pass) { 289 + run("Running tests under ptrace: %s\n", ptrace_pass_name[pass]); 290 + test_syscall_numbering(); 291 + } 292 + } 293 + 294 + static void mess_with_syscall(pid_t testpid, enum ptrace_pass pass) 295 + { 296 + struct user_regs_struct regs; 297 + 298 + sh->probing_syscall = false; /* Do this on entry only */ 299 + 300 + /* For these, don't even getregs */ 301 + if (pass == PTP_NOTHING || pass == PTP_DONE) 302 + return; 303 + 304 + ptrace(PTRACE_GETREGS, testpid, NULL, &regs); 305 + 306 + if (regs.orig_rax != regs.rbx) { 307 + fail("orig_rax %#llx doesn't match syscall number %#llx\n", 308 + (unsigned long long)regs.orig_rax, 309 + (unsigned long long)regs.rbx); 310 + } 311 + 312 + switch (pass) { 313 + case PTP_GETREGS: 314 + /* Just read, no writeback */ 315 + return; 316 + case PTP_WRITEBACK: 317 + /* Write back the same register state verbatim */ 318 + break; 319 + case PTP_FUZZRET: 320 + regs.rax = MODIFIED_BY_PTRACE; 321 + break; 322 + case PTP_FUZZHIGH: 323 + regs.rax = MODIFIED_BY_PTRACE; 324 + regs.orig_rax = regs.orig_rax | 0xffffffff00000000ULL; 325 + break; 326 + case PTP_INTNUM: 327 + regs.rax = MODIFIED_BY_PTRACE; 328 + regs.orig_rax = (int)regs.orig_rax; 329 + break; 330 + default: 331 + crit("invalid ptrace_pass\n"); 332 + break; 333 + } 334 + 335 + ptrace(PTRACE_SETREGS, testpid, NULL, &regs); 336 + } 337 + 338 + static void syscall_numbering_tracer(pid_t testpid) 339 + { 340 + int wstatus; 341 + 342 + do { 343 + pid_t wpid = waitpid(testpid, &wstatus, 0); 344 + if (wpid < 0 && errno != EINTR) 345 + break; 346 + if (wpid != testpid) 347 + continue; 348 + if (!WIFSTOPPED(wstatus)) 349 + break; /* Thread exited? */ 350 + 351 + if (sh->probing_syscall && WSTOPSIG(wstatus) == SIGTRAP) 352 + mess_with_syscall(testpid, sh->ptrace_pass); 353 + } while (sh->ptrace_pass != PTP_DONE && 354 + !ptrace(PTRACE_SYSCALL, testpid, NULL, NULL)); 355 + 356 + ptrace(PTRACE_DETACH, testpid, NULL, NULL); 357 + 358 + /* Wait for the child process to terminate */ 359 + while (waitpid(testpid, &wstatus, 0) != testpid || !WIFEXITED(wstatus)) 360 + /* wait some more */; 361 + } 362 + 363 + static void test_traced_syscall_numbering(void) 364 + { 365 + pid_t testpid; 366 + 367 + /* Launch the test thread; this thread continues as the tracer thread */ 368 + testpid = fork(); 369 + 370 + if (testpid < 0) { 371 + crit("Unable to launch tracer process\n"); 372 + } else if (testpid == 0) { 373 + syscall_numbering_tracee(); 374 + _exit(0); 375 + } else { 376 + syscall_numbering_tracer(testpid); 330 377 } 331 378 } 332 379 333 380 int main(void) 334 381 { 382 + unsigned int nerr; 383 + 335 384 /* 336 385 * It is quite likely to get a segfault on a failure, so make 337 386 * sure the message gets out by setting stdout to nonbuffered. ··· 453 288 crit("Unable to open /dev/null: %s\n", strerror(errno)); 454 289 } 455 290 291 + /* 292 + * Set up a block of shared memory... 293 + */ 294 + sh = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ|PROT_WRITE, 295 + MAP_ANONYMOUS|MAP_SHARED, 0, 0); 296 + if (sh == MAP_FAILED) { 297 + crit("Unable to allocated shared memory block: %s\n", 298 + strerror(errno)); 299 + } 300 + 301 + with_x32 = test_x32(); 302 + 303 + run("Running tests without ptrace...\n"); 456 304 test_syscall_numbering(); 305 + 306 + test_traced_syscall_numbering(); 307 + 308 + nerr = sh->nerr; 457 309 if (!nerr) { 458 310 ok("All system calls succeeded or failed as expected\n"); 459 311 return 0;