Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * A ptrace test for testing PTRACE_SYSEMU, PTRACE_SETREGS and
0004  * PTRACE_GETREG.  This test basically create a child process that executes
0005  * syscalls and the parent process check if it is being traced appropriated.
0006  *
0007  * This test is heavily based on tools/testing/selftests/x86/ptrace_syscall.c
0008  * test, and it was adapted to run on Powerpc by
0009  * Breno Leitao <leitao@debian.org>
0010  */
0011 #define _GNU_SOURCE
0012 
0013 #include <sys/ptrace.h>
0014 #include <sys/types.h>
0015 #include <sys/wait.h>
0016 #include <sys/syscall.h>
0017 #include <sys/user.h>
0018 #include <unistd.h>
0019 #include <errno.h>
0020 #include <stddef.h>
0021 #include <stdio.h>
0022 #include <err.h>
0023 #include <string.h>
0024 #include <sys/auxv.h>
0025 #include "utils.h"
0026 
0027 /* Bitness-agnostic defines for user_regs_struct fields. */
0028 #define user_syscall_nr gpr[0]
0029 #define user_arg0       gpr[3]
0030 #define user_arg1       gpr[4]
0031 #define user_arg2       gpr[5]
0032 #define user_arg3       gpr[6]
0033 #define user_arg4       gpr[7]
0034 #define user_arg5       gpr[8]
0035 #define user_ip     nip
0036 
0037 #define PTRACE_SYSEMU       0x1d
0038 
0039 static int nerrs;
0040 
0041 static void wait_trap(pid_t chld)
0042 {
0043     siginfo_t si;
0044 
0045     if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0)
0046         err(1, "waitid");
0047     if (si.si_pid != chld)
0048         errx(1, "got unexpected pid in event\n");
0049     if (si.si_code != CLD_TRAPPED)
0050         errx(1, "got unexpected event type %d\n", si.si_code);
0051 }
0052 
0053 static void test_ptrace_syscall_restart(void)
0054 {
0055     int status;
0056     struct pt_regs regs;
0057     pid_t chld;
0058 
0059     printf("[RUN]\tptrace-induced syscall restart\n");
0060 
0061     chld = fork();
0062     if (chld < 0)
0063         err(1, "fork");
0064 
0065     /*
0066      * Child process is running 4 syscalls after ptrace.
0067      *
0068      * 1) getpid()
0069      * 2) gettid()
0070      * 3) tgkill() -> Send SIGSTOP
0071      * 4) gettid() -> Where the tests will happen essentially
0072      */
0073     if (chld == 0) {
0074         if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
0075             err(1, "PTRACE_TRACEME");
0076 
0077         pid_t pid = getpid(), tid = syscall(SYS_gettid);
0078 
0079         printf("\tChild will make one syscall\n");
0080         syscall(SYS_tgkill, pid, tid, SIGSTOP);
0081 
0082         syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
0083         _exit(0);
0084     }
0085     /* Parent process below */
0086 
0087     /* Wait for SIGSTOP sent by tgkill above. */
0088     if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
0089         err(1, "waitpid");
0090 
0091     printf("[RUN]\tSYSEMU\n");
0092     if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
0093         err(1, "PTRACE_SYSEMU");
0094     wait_trap(chld);
0095 
0096     if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
0097         err(1, "PTRACE_GETREGS");
0098 
0099     /*
0100      * Ptrace trapped prior to executing the syscall, thus r3 still has
0101      * the syscall number instead of the sys_gettid() result
0102      */
0103     if (regs.user_syscall_nr != SYS_gettid ||
0104         regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
0105         regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
0106         regs.user_arg4 != 14 || regs.user_arg5 != 15) {
0107         printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
0108             (unsigned long)regs.user_syscall_nr,
0109             (unsigned long)regs.user_arg0,
0110             (unsigned long)regs.user_arg1,
0111             (unsigned long)regs.user_arg2,
0112             (unsigned long)regs.user_arg3,
0113             (unsigned long)regs.user_arg4,
0114             (unsigned long)regs.user_arg5);
0115          nerrs++;
0116     } else {
0117         printf("[OK]\tInitial nr and args are correct\n"); }
0118 
0119     printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n",
0120            (unsigned long)regs.user_ip);
0121 
0122     /*
0123      * Rewind to retry the same syscall again. This will basically test
0124      * the rewind process together with PTRACE_SETREGS and PTRACE_GETREGS.
0125      */
0126     regs.user_ip -= 4;
0127     if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
0128         err(1, "PTRACE_SETREGS");
0129 
0130     if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
0131         err(1, "PTRACE_SYSEMU");
0132     wait_trap(chld);
0133 
0134     if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
0135         err(1, "PTRACE_GETREGS");
0136 
0137     if (regs.user_syscall_nr != SYS_gettid ||
0138         regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
0139         regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
0140         regs.user_arg4 != 14 || regs.user_arg5 != 15) {
0141         printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
0142             (unsigned long)regs.user_syscall_nr,
0143             (unsigned long)regs.user_arg0,
0144             (unsigned long)regs.user_arg1,
0145             (unsigned long)regs.user_arg2,
0146             (unsigned long)regs.user_arg3,
0147             (unsigned long)regs.user_arg4,
0148             (unsigned long)regs.user_arg5);
0149         nerrs++;
0150     } else {
0151         printf("[OK]\tRestarted nr and args are correct\n");
0152     }
0153 
0154     printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n",
0155            (unsigned long)regs.user_ip);
0156 
0157     /*
0158      * Inject a new syscall (getpid) in the same place the previous
0159      * syscall (gettid), rewind and re-execute.
0160      */
0161     regs.user_syscall_nr = SYS_getpid;
0162     regs.user_arg0 = 20;
0163     regs.user_arg1 = 21;
0164     regs.user_arg2 = 22;
0165     regs.user_arg3 = 23;
0166     regs.user_arg4 = 24;
0167     regs.user_arg5 = 25;
0168     regs.user_ip -= 4;
0169 
0170     if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
0171         err(1, "PTRACE_SETREGS");
0172 
0173     if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
0174         err(1, "PTRACE_SYSEMU");
0175     wait_trap(chld);
0176 
0177     if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
0178         err(1, "PTRACE_GETREGS");
0179 
0180     /* Check that ptrace stopped at the new syscall that was
0181      * injected, and guarantee that it haven't executed, i.e, user_args
0182      * contain the arguments and not the syscall return value, for
0183      * instance.
0184      */
0185     if (regs.user_syscall_nr != SYS_getpid
0186         || regs.user_arg0 != 20 || regs.user_arg1 != 21
0187         || regs.user_arg2 != 22 || regs.user_arg3 != 23
0188         || regs.user_arg4 != 24 || regs.user_arg5 != 25) {
0189 
0190         printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
0191             (unsigned long)regs.user_syscall_nr,
0192             (unsigned long)regs.user_arg0,
0193             (unsigned long)regs.user_arg1,
0194             (unsigned long)regs.user_arg2,
0195             (unsigned long)regs.user_arg3,
0196             (unsigned long)regs.user_arg4,
0197             (unsigned long)regs.user_arg5);
0198         nerrs++;
0199     } else {
0200         printf("[OK]\tReplacement nr and args are correct\n");
0201     }
0202 
0203     if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
0204         err(1, "PTRACE_CONT");
0205 
0206     if (waitpid(chld, &status, 0) != chld)
0207         err(1, "waitpid");
0208 
0209     /* Guarantee that the process executed properly, returning 0 */
0210     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
0211         printf("[FAIL]\tChild failed\n");
0212         nerrs++;
0213     } else {
0214         printf("[OK]\tChild exited cleanly\n");
0215     }
0216 }
0217 
0218 int ptrace_syscall(void)
0219 {
0220     test_ptrace_syscall_restart();
0221 
0222     return nerrs;
0223 }
0224 
0225 int main(void)
0226 {
0227     return test_harness(ptrace_syscall, "ptrace_syscall");
0228 }