root/tools/testing/selftests/x86/ptrace_syscall.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. do_full_int80
  2. do_full_vsyscall32
  3. wait_trap
  4. sethandler
  5. setsigign
  6. clearhandler
  7. empty_handler
  8. test_sys32_regs
  9. test_ptrace_syscall_restart
  10. test_restart_under_ptrace
  11. main

   1 // SPDX-License-Identifier: GPL-2.0
   2 #define _GNU_SOURCE
   3 
   4 #include <sys/ptrace.h>
   5 #include <sys/types.h>
   6 #include <sys/wait.h>
   7 #include <sys/syscall.h>
   8 #include <sys/user.h>
   9 #include <unistd.h>
  10 #include <errno.h>
  11 #include <stddef.h>
  12 #include <stdio.h>
  13 #include <err.h>
  14 #include <string.h>
  15 #include <asm/ptrace-abi.h>
  16 #include <sys/auxv.h>
  17 
  18 /* Bitness-agnostic defines for user_regs_struct fields. */
  19 #ifdef __x86_64__
  20 # define user_syscall_nr        orig_rax
  21 # define user_arg0              rdi
  22 # define user_arg1              rsi
  23 # define user_arg2              rdx
  24 # define user_arg3              r10
  25 # define user_arg4              r8
  26 # define user_arg5              r9
  27 # define user_ip                rip
  28 # define user_ax                rax
  29 #else
  30 # define user_syscall_nr        orig_eax
  31 # define user_arg0              ebx
  32 # define user_arg1              ecx
  33 # define user_arg2              edx
  34 # define user_arg3              esi
  35 # define user_arg4              edi
  36 # define user_arg5              ebp
  37 # define user_ip                eip
  38 # define user_ax                eax
  39 #endif
  40 
  41 static int nerrs = 0;
  42 
  43 struct syscall_args32 {
  44         uint32_t nr, arg0, arg1, arg2, arg3, arg4, arg5;
  45 };
  46 
  47 #ifdef __i386__
  48 extern void sys32_helper(struct syscall_args32 *, void *);
  49 extern void int80_and_ret(void);
  50 #endif
  51 
  52 /*
  53  * Helper to invoke int80 with controlled regs and capture the final regs.
  54  */
  55 static void do_full_int80(struct syscall_args32 *args)
  56 {
  57 #ifdef __x86_64__
  58         register unsigned long bp asm("bp") = args->arg5;
  59         asm volatile ("int $0x80"
  60                       : "+a" (args->nr),
  61                         "+b" (args->arg0), "+c" (args->arg1), "+d" (args->arg2),
  62                         "+S" (args->arg3), "+D" (args->arg4), "+r" (bp)
  63                         : : "r8", "r9", "r10", "r11");
  64         args->arg5 = bp;
  65 #else
  66         sys32_helper(args, int80_and_ret);
  67 #endif
  68 }
  69 
  70 #ifdef __i386__
  71 static void (*vsyscall32)(void);
  72 
  73 /*
  74  * Nasty helper to invoke AT_SYSINFO (i.e. __kernel_vsyscall) with
  75  * controlled regs and capture the final regs.  This is so nasty that it
  76  * crashes my copy of gdb :)
  77  */
  78 static void do_full_vsyscall32(struct syscall_args32 *args)
  79 {
  80         sys32_helper(args, vsyscall32);
  81 }
  82 #endif
  83 
  84 static siginfo_t wait_trap(pid_t chld)
  85 {
  86         siginfo_t si;
  87         if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0)
  88                 err(1, "waitid");
  89         if (si.si_pid != chld)
  90                 errx(1, "got unexpected pid in event\n");
  91         if (si.si_code != CLD_TRAPPED)
  92                 errx(1, "got unexpected event type %d\n", si.si_code);
  93         return si;
  94 }
  95 
  96 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
  97                        int flags)
  98 {
  99         struct sigaction sa;
 100         memset(&sa, 0, sizeof(sa));
 101         sa.sa_sigaction = handler;
 102         sa.sa_flags = SA_SIGINFO | flags;
 103         sigemptyset(&sa.sa_mask);
 104         if (sigaction(sig, &sa, 0))
 105                 err(1, "sigaction");
 106 }
 107 
 108 static void setsigign(int sig, int flags)
 109 {
 110         struct sigaction sa;
 111         memset(&sa, 0, sizeof(sa));
 112         sa.sa_sigaction = (void *)SIG_IGN;
 113         sa.sa_flags = flags;
 114         sigemptyset(&sa.sa_mask);
 115         if (sigaction(sig, &sa, 0))
 116                 err(1, "sigaction");
 117 }
 118 
 119 static void clearhandler(int sig)
 120 {
 121         struct sigaction sa;
 122         memset(&sa, 0, sizeof(sa));
 123         sa.sa_handler = SIG_DFL;
 124         sigemptyset(&sa.sa_mask);
 125         if (sigaction(sig, &sa, 0))
 126                 err(1, "sigaction");
 127 }
 128 
 129 #ifdef __x86_64__
 130 # define REG_BP REG_RBP
 131 #else
 132 # define REG_BP REG_EBP
 133 #endif
 134 
 135 static void empty_handler(int sig, siginfo_t *si, void *ctx_void)
 136 {
 137 }
 138 
 139 static void test_sys32_regs(void (*do_syscall)(struct syscall_args32 *))
 140 {
 141         struct syscall_args32 args = {
 142                 .nr = 224,      /* gettid */
 143                 .arg0 = 10, .arg1 = 11, .arg2 = 12,
 144                 .arg3 = 13, .arg4 = 14, .arg5 = 15,
 145         };
 146 
 147         do_syscall(&args);
 148 
 149         if (args.nr != getpid() ||
 150             args.arg0 != 10 || args.arg1 != 11 || args.arg2 != 12 ||
 151             args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
 152                 printf("[FAIL]\tgetpid() failed to preserve regs\n");
 153                 nerrs++;
 154         } else {
 155                 printf("[OK]\tgetpid() preserves regs\n");
 156         }
 157 
 158         sethandler(SIGUSR1, empty_handler, 0);
 159 
 160         args.nr = 37;   /* kill */
 161         args.arg0 = getpid();
 162         args.arg1 = SIGUSR1;
 163         do_syscall(&args);
 164         if (args.nr != 0 ||
 165             args.arg0 != getpid() || args.arg1 != SIGUSR1 || args.arg2 != 12 ||
 166             args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
 167                 printf("[FAIL]\tkill(getpid(), SIGUSR1) failed to preserve regs\n");
 168                 nerrs++;
 169         } else {
 170                 printf("[OK]\tkill(getpid(), SIGUSR1) preserves regs\n");
 171         }
 172         clearhandler(SIGUSR1);
 173 }
 174 
 175 static void test_ptrace_syscall_restart(void)
 176 {
 177         printf("[RUN]\tptrace-induced syscall restart\n");
 178         pid_t chld = fork();
 179         if (chld < 0)
 180                 err(1, "fork");
 181 
 182         if (chld == 0) {
 183                 if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
 184                         err(1, "PTRACE_TRACEME");
 185 
 186                 pid_t pid = getpid(), tid = syscall(SYS_gettid);
 187 
 188                 printf("\tChild will make one syscall\n");
 189                 syscall(SYS_tgkill, pid, tid, SIGSTOP);
 190 
 191                 syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
 192                 _exit(0);
 193         }
 194 
 195         int status;
 196 
 197         /* Wait for SIGSTOP. */
 198         if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
 199                 err(1, "waitpid");
 200 
 201         struct user_regs_struct regs;
 202 
 203         printf("[RUN]\tSYSEMU\n");
 204         if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
 205                 err(1, "PTRACE_SYSEMU");
 206         wait_trap(chld);
 207 
 208         if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
 209                 err(1, "PTRACE_GETREGS");
 210 
 211         if (regs.user_syscall_nr != SYS_gettid ||
 212             regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
 213             regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
 214             regs.user_arg4 != 14 || regs.user_arg5 != 15) {
 215                 printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
 216                 nerrs++;
 217         } else {
 218                 printf("[OK]\tInitial nr and args are correct\n");
 219         }
 220 
 221         printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n",
 222                (unsigned long)regs.user_ip);
 223 
 224         /*
 225          * This does exactly what it appears to do if syscall is int80 or
 226          * SYSCALL64.  For SYSCALL32 or SYSENTER, though, this is highly
 227          * magical.  It needs to work so that ptrace and syscall restart
 228          * work as expected.
 229          */
 230         regs.user_ax = regs.user_syscall_nr;
 231         regs.user_ip -= 2;
 232         if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
 233                 err(1, "PTRACE_SETREGS");
 234 
 235         if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
 236                 err(1, "PTRACE_SYSEMU");
 237         wait_trap(chld);
 238 
 239         if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
 240                 err(1, "PTRACE_GETREGS");
 241 
 242         if (regs.user_syscall_nr != SYS_gettid ||
 243             regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
 244             regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
 245             regs.user_arg4 != 14 || regs.user_arg5 != 15) {
 246                 printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
 247                 nerrs++;
 248         } else {
 249                 printf("[OK]\tRestarted nr and args are correct\n");
 250         }
 251 
 252         printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n",
 253                (unsigned long)regs.user_ip);
 254 
 255         regs.user_ax = SYS_getpid;
 256         regs.user_arg0 = 20;
 257         regs.user_arg1 = 21;
 258         regs.user_arg2 = 22;
 259         regs.user_arg3 = 23;
 260         regs.user_arg4 = 24;
 261         regs.user_arg5 = 25;
 262         regs.user_ip -= 2;
 263 
 264         if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
 265                 err(1, "PTRACE_SETREGS");
 266 
 267         if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
 268                 err(1, "PTRACE_SYSEMU");
 269         wait_trap(chld);
 270 
 271         if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
 272                 err(1, "PTRACE_GETREGS");
 273 
 274         if (regs.user_syscall_nr != SYS_getpid ||
 275             regs.user_arg0 != 20 || regs.user_arg1 != 21 || regs.user_arg2 != 22 ||
 276             regs.user_arg3 != 23 || regs.user_arg4 != 24 || regs.user_arg5 != 25) {
 277                 printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
 278                 nerrs++;
 279         } else {
 280                 printf("[OK]\tReplacement nr and args are correct\n");
 281         }
 282 
 283         if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
 284                 err(1, "PTRACE_CONT");
 285         if (waitpid(chld, &status, 0) != chld)
 286                 err(1, "waitpid");
 287         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
 288                 printf("[FAIL]\tChild failed\n");
 289                 nerrs++;
 290         } else {
 291                 printf("[OK]\tChild exited cleanly\n");
 292         }
 293 }
 294 
 295 static void test_restart_under_ptrace(void)
 296 {
 297         printf("[RUN]\tkernel syscall restart under ptrace\n");
 298         pid_t chld = fork();
 299         if (chld < 0)
 300                 err(1, "fork");
 301 
 302         if (chld == 0) {
 303                 if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
 304                         err(1, "PTRACE_TRACEME");
 305 
 306                 pid_t pid = getpid(), tid = syscall(SYS_gettid);
 307 
 308                 printf("\tChild will take a nap until signaled\n");
 309                 setsigign(SIGUSR1, SA_RESTART);
 310                 syscall(SYS_tgkill, pid, tid, SIGSTOP);
 311 
 312                 syscall(SYS_pause, 0, 0, 0, 0, 0, 0);
 313                 _exit(0);
 314         }
 315 
 316         int status;
 317 
 318         /* Wait for SIGSTOP. */
 319         if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
 320                 err(1, "waitpid");
 321 
 322         struct user_regs_struct regs;
 323 
 324         printf("[RUN]\tSYSCALL\n");
 325         if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0)
 326                 err(1, "PTRACE_SYSCALL");
 327         wait_trap(chld);
 328 
 329         /* We should be stopped at pause(2) entry. */
 330 
 331         if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
 332                 err(1, "PTRACE_GETREGS");
 333 
 334         if (regs.user_syscall_nr != SYS_pause ||
 335             regs.user_arg0 != 0 || regs.user_arg1 != 0 ||
 336             regs.user_arg2 != 0 || regs.user_arg3 != 0 ||
 337             regs.user_arg4 != 0 || regs.user_arg5 != 0) {
 338                 printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
 339                 nerrs++;
 340         } else {
 341                 printf("[OK]\tInitial nr and args are correct\n");
 342         }
 343 
 344         /* Interrupt it. */
 345         kill(chld, SIGUSR1);
 346 
 347         /* Advance.  We should be stopped at exit. */
 348         printf("[RUN]\tSYSCALL\n");
 349         if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0)
 350                 err(1, "PTRACE_SYSCALL");
 351         wait_trap(chld);
 352 
 353         if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
 354                 err(1, "PTRACE_GETREGS");
 355 
 356         if (regs.user_syscall_nr != SYS_pause ||
 357             regs.user_arg0 != 0 || regs.user_arg1 != 0 ||
 358             regs.user_arg2 != 0 || regs.user_arg3 != 0 ||
 359             regs.user_arg4 != 0 || regs.user_arg5 != 0) {
 360                 printf("[FAIL]\tArgs after SIGUSR1 are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
 361                 nerrs++;
 362         } else {
 363                 printf("[OK]\tArgs after SIGUSR1 are correct (ax = %ld)\n",
 364                        (long)regs.user_ax);
 365         }
 366 
 367         /* Poke the regs back in.  This must not break anything. */
 368         if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
 369                 err(1, "PTRACE_SETREGS");
 370 
 371         /* Catch the (ignored) SIGUSR1. */
 372         if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
 373                 err(1, "PTRACE_CONT");
 374         if (waitpid(chld, &status, 0) != chld)
 375                 err(1, "waitpid");
 376         if (!WIFSTOPPED(status)) {
 377                 printf("[FAIL]\tChild was stopped for SIGUSR1 (status = 0x%x)\n", status);
 378                 nerrs++;
 379         } else {
 380                 printf("[OK]\tChild got SIGUSR1\n");
 381         }
 382 
 383         /* The next event should be pause(2) again. */
 384         printf("[RUN]\tStep again\n");
 385         if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0)
 386                 err(1, "PTRACE_SYSCALL");
 387         wait_trap(chld);
 388 
 389         /* We should be stopped at pause(2) entry. */
 390 
 391         if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
 392                 err(1, "PTRACE_GETREGS");
 393 
 394         if (regs.user_syscall_nr != SYS_pause ||
 395             regs.user_arg0 != 0 || regs.user_arg1 != 0 ||
 396             regs.user_arg2 != 0 || regs.user_arg3 != 0 ||
 397             regs.user_arg4 != 0 || regs.user_arg5 != 0) {
 398                 printf("[FAIL]\tpause did not restart (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
 399                 nerrs++;
 400         } else {
 401                 printf("[OK]\tpause(2) restarted correctly\n");
 402         }
 403 
 404         /* Kill it. */
 405         kill(chld, SIGKILL);
 406         if (waitpid(chld, &status, 0) != chld)
 407                 err(1, "waitpid");
 408 }
 409 
 410 int main()
 411 {
 412         printf("[RUN]\tCheck int80 return regs\n");
 413         test_sys32_regs(do_full_int80);
 414 
 415 #if defined(__i386__) && (!defined(__GLIBC__) || __GLIBC__ > 2 || __GLIBC_MINOR__ >= 16)
 416         vsyscall32 = (void *)getauxval(AT_SYSINFO);
 417         if (vsyscall32) {
 418                 printf("[RUN]\tCheck AT_SYSINFO return regs\n");
 419                 test_sys32_regs(do_full_vsyscall32);
 420         } else {
 421                 printf("[SKIP]\tAT_SYSINFO is not available\n");
 422         }
 423 #endif
 424 
 425         test_ptrace_syscall_restart();
 426 
 427         test_restart_under_ptrace();
 428 
 429         return 0;
 430 }

/* [<][>][^][v][top][bottom][index][help] */