ptrace-syscall.c (6239B)
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 39static int nerrs; 40 41static 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 53static 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, ®s) != 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, ®s) != 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, ®s) != 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, ®s) != 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, ®s) != 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 218int ptrace_syscall(void) 219{ 220 test_ptrace_syscall_restart(); 221 222 return nerrs; 223} 224 225int main(void) 226{ 227 return test_harness(ptrace_syscall, "ptrace_syscall"); 228}