root/tools/testing/selftests/powerpc/ptrace/ptrace-syscall.c

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

DEFINITIONS

This source file includes following definitions.
  1. wait_trap
  2. test_ptrace_syscall_restart
  3. ptrace_syscall
  4. main

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * A ptrace test for testing PTRACE_SYSEMU, PTRACE_SETREGS and
   4  * PTRACE_GETREG.  This test basically create a child process that executes
   5  * syscalls and the parent process check if it is being traced appropriated.
   6  *
   7  * This test is heavily based on tools/testing/selftests/x86/ptrace_syscall.c
   8  * test, and it was adapted to run on Powerpc by
   9  * Breno Leitao <leitao@debian.org>
  10  */
  11 #define _GNU_SOURCE
  12 
  13 #include <sys/ptrace.h>
  14 #include <sys/types.h>
  15 #include <sys/wait.h>
  16 #include <sys/syscall.h>
  17 #include <sys/user.h>
  18 #include <unistd.h>
  19 #include <errno.h>
  20 #include <stddef.h>
  21 #include <stdio.h>
  22 #include <err.h>
  23 #include <string.h>
  24 #include <sys/auxv.h>
  25 #include "utils.h"
  26 
  27 /* Bitness-agnostic defines for user_regs_struct fields. */
  28 #define user_syscall_nr gpr[0]
  29 #define user_arg0               gpr[3]
  30 #define user_arg1               gpr[4]
  31 #define user_arg2               gpr[5]
  32 #define user_arg3               gpr[6]
  33 #define user_arg4               gpr[7]
  34 #define user_arg5               gpr[8]
  35 #define user_ip         nip
  36 
  37 #define PTRACE_SYSEMU           0x1d
  38 
  39 static int nerrs;
  40 
  41 static void wait_trap(pid_t chld)
  42 {
  43         siginfo_t si;
  44 
  45         if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0)
  46                 err(1, "waitid");
  47         if (si.si_pid != chld)
  48                 errx(1, "got unexpected pid in event\n");
  49         if (si.si_code != CLD_TRAPPED)
  50                 errx(1, "got unexpected event type %d\n", si.si_code);
  51 }
  52 
  53 static void test_ptrace_syscall_restart(void)
  54 {
  55         int status;
  56         struct pt_regs regs;
  57         pid_t chld;
  58 
  59         printf("[RUN]\tptrace-induced syscall restart\n");
  60 
  61         chld = fork();
  62         if (chld < 0)
  63                 err(1, "fork");
  64 
  65         /*
  66          * Child process is running 4 syscalls after ptrace.
  67          *
  68          * 1) getpid()
  69          * 2) gettid()
  70          * 3) tgkill() -> Send SIGSTOP
  71          * 4) gettid() -> Where the tests will happen essentially
  72          */
  73         if (chld == 0) {
  74                 if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
  75                         err(1, "PTRACE_TRACEME");
  76 
  77                 pid_t pid = getpid(), tid = syscall(SYS_gettid);
  78 
  79                 printf("\tChild will make one syscall\n");
  80                 syscall(SYS_tgkill, pid, tid, SIGSTOP);
  81 
  82                 syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
  83                 _exit(0);
  84         }
  85         /* Parent process below */
  86 
  87         /* Wait for SIGSTOP sent by tgkill above. */
  88         if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
  89                 err(1, "waitpid");
  90 
  91         printf("[RUN]\tSYSEMU\n");
  92         if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
  93                 err(1, "PTRACE_SYSEMU");
  94         wait_trap(chld);
  95 
  96         if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
  97                 err(1, "PTRACE_GETREGS");
  98 
  99         /*
 100          * Ptrace trapped prior to executing the syscall, thus r3 still has
 101          * the syscall number instead of the sys_gettid() result
 102          */
 103         if (regs.user_syscall_nr != SYS_gettid ||
 104             regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
 105             regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
 106             regs.user_arg4 != 14 || regs.user_arg5 != 15) {
 107                 printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
 108                         (unsigned long)regs.user_syscall_nr,
 109                         (unsigned long)regs.user_arg0,
 110                         (unsigned long)regs.user_arg1,
 111                         (unsigned long)regs.user_arg2,
 112                         (unsigned long)regs.user_arg3,
 113                         (unsigned long)regs.user_arg4,
 114                         (unsigned long)regs.user_arg5);
 115                  nerrs++;
 116         } else {
 117                 printf("[OK]\tInitial nr and args are correct\n"); }
 118 
 119         printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n",
 120                (unsigned long)regs.user_ip);
 121 
 122         /*
 123          * Rewind to retry the same syscall again. This will basically test
 124          * the rewind process together with PTRACE_SETREGS and PTRACE_GETREGS.
 125          */
 126         regs.user_ip -= 4;
 127         if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
 128                 err(1, "PTRACE_SETREGS");
 129 
 130         if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
 131                 err(1, "PTRACE_SYSEMU");
 132         wait_trap(chld);
 133 
 134         if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
 135                 err(1, "PTRACE_GETREGS");
 136 
 137         if (regs.user_syscall_nr != SYS_gettid ||
 138             regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
 139             regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
 140             regs.user_arg4 != 14 || regs.user_arg5 != 15) {
 141                 printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
 142                         (unsigned long)regs.user_syscall_nr,
 143                         (unsigned long)regs.user_arg0,
 144                         (unsigned long)regs.user_arg1,
 145                         (unsigned long)regs.user_arg2,
 146                         (unsigned long)regs.user_arg3,
 147                         (unsigned long)regs.user_arg4,
 148                         (unsigned long)regs.user_arg5);
 149                 nerrs++;
 150         } else {
 151                 printf("[OK]\tRestarted nr and args are correct\n");
 152         }
 153 
 154         printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n",
 155                (unsigned long)regs.user_ip);
 156 
 157         /*
 158          * Inject a new syscall (getpid) in the same place the previous
 159          * syscall (gettid), rewind and re-execute.
 160          */
 161         regs.user_syscall_nr = SYS_getpid;
 162         regs.user_arg0 = 20;
 163         regs.user_arg1 = 21;
 164         regs.user_arg2 = 22;
 165         regs.user_arg3 = 23;
 166         regs.user_arg4 = 24;
 167         regs.user_arg5 = 25;
 168         regs.user_ip -= 4;
 169 
 170         if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
 171                 err(1, "PTRACE_SETREGS");
 172 
 173         if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
 174                 err(1, "PTRACE_SYSEMU");
 175         wait_trap(chld);
 176 
 177         if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
 178                 err(1, "PTRACE_GETREGS");
 179 
 180         /* Check that ptrace stopped at the new syscall that was
 181          * injected, and guarantee that it haven't executed, i.e, user_args
 182          * contain the arguments and not the syscall return value, for
 183          * instance.
 184          */
 185         if (regs.user_syscall_nr != SYS_getpid
 186                 || regs.user_arg0 != 20 || regs.user_arg1 != 21
 187                 || regs.user_arg2 != 22 || regs.user_arg3 != 23
 188                 || regs.user_arg4 != 24 || regs.user_arg5 != 25) {
 189 
 190                 printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
 191                         (unsigned long)regs.user_syscall_nr,
 192                         (unsigned long)regs.user_arg0,
 193                         (unsigned long)regs.user_arg1,
 194                         (unsigned long)regs.user_arg2,
 195                         (unsigned long)regs.user_arg3,
 196                         (unsigned long)regs.user_arg4,
 197                         (unsigned long)regs.user_arg5);
 198                 nerrs++;
 199         } else {
 200                 printf("[OK]\tReplacement nr and args are correct\n");
 201         }
 202 
 203         if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
 204                 err(1, "PTRACE_CONT");
 205 
 206         if (waitpid(chld, &status, 0) != chld)
 207                 err(1, "waitpid");
 208 
 209         /* Guarantee that the process executed properly, returning 0 */
 210         if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
 211                 printf("[FAIL]\tChild failed\n");
 212                 nerrs++;
 213         } else {
 214                 printf("[OK]\tChild exited cleanly\n");
 215         }
 216 }
 217 
 218 int ptrace_syscall(void)
 219 {
 220         test_ptrace_syscall_restart();
 221 
 222         return nerrs;
 223 }
 224 
 225 int main(void)
 226 {
 227         return test_harness(ptrace_syscall, "ptrace_syscall");
 228 }

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