162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * A ptrace test for testing PTRACE_SYSEMU, PTRACE_SETREGS and 462306a36Sopenharmony_ci * PTRACE_GETREG. This test basically create a child process that executes 562306a36Sopenharmony_ci * syscalls and the parent process check if it is being traced appropriated. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This test is heavily based on tools/testing/selftests/x86/ptrace_syscall.c 862306a36Sopenharmony_ci * test, and it was adapted to run on Powerpc by 962306a36Sopenharmony_ci * Breno Leitao <leitao@debian.org> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#define _GNU_SOURCE 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <sys/ptrace.h> 1462306a36Sopenharmony_ci#include <sys/types.h> 1562306a36Sopenharmony_ci#include <sys/wait.h> 1662306a36Sopenharmony_ci#include <sys/syscall.h> 1762306a36Sopenharmony_ci#include <sys/user.h> 1862306a36Sopenharmony_ci#include <unistd.h> 1962306a36Sopenharmony_ci#include <errno.h> 2062306a36Sopenharmony_ci#include <stddef.h> 2162306a36Sopenharmony_ci#include <stdio.h> 2262306a36Sopenharmony_ci#include <err.h> 2362306a36Sopenharmony_ci#include <string.h> 2462306a36Sopenharmony_ci#include <sys/auxv.h> 2562306a36Sopenharmony_ci#include "utils.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Bitness-agnostic defines for user_regs_struct fields. */ 2862306a36Sopenharmony_ci#define user_syscall_nr gpr[0] 2962306a36Sopenharmony_ci#define user_arg0 gpr[3] 3062306a36Sopenharmony_ci#define user_arg1 gpr[4] 3162306a36Sopenharmony_ci#define user_arg2 gpr[5] 3262306a36Sopenharmony_ci#define user_arg3 gpr[6] 3362306a36Sopenharmony_ci#define user_arg4 gpr[7] 3462306a36Sopenharmony_ci#define user_arg5 gpr[8] 3562306a36Sopenharmony_ci#define user_ip nip 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define PTRACE_SYSEMU 0x1d 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int nerrs; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void wait_trap(pid_t chld) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci siginfo_t si; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0) 4662306a36Sopenharmony_ci err(1, "waitid"); 4762306a36Sopenharmony_ci if (si.si_pid != chld) 4862306a36Sopenharmony_ci errx(1, "got unexpected pid in event\n"); 4962306a36Sopenharmony_ci if (si.si_code != CLD_TRAPPED) 5062306a36Sopenharmony_ci errx(1, "got unexpected event type %d\n", si.si_code); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void test_ptrace_syscall_restart(void) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci int status; 5662306a36Sopenharmony_ci struct pt_regs regs; 5762306a36Sopenharmony_ci pid_t chld; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci printf("[RUN]\tptrace-induced syscall restart\n"); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci chld = fork(); 6262306a36Sopenharmony_ci if (chld < 0) 6362306a36Sopenharmony_ci err(1, "fork"); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * Child process is running 4 syscalls after ptrace. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * 1) getpid() 6962306a36Sopenharmony_ci * 2) gettid() 7062306a36Sopenharmony_ci * 3) tgkill() -> Send SIGSTOP 7162306a36Sopenharmony_ci * 4) gettid() -> Where the tests will happen essentially 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci if (chld == 0) { 7462306a36Sopenharmony_ci if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) 7562306a36Sopenharmony_ci err(1, "PTRACE_TRACEME"); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci pid_t pid = getpid(), tid = syscall(SYS_gettid); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci printf("\tChild will make one syscall\n"); 8062306a36Sopenharmony_ci syscall(SYS_tgkill, pid, tid, SIGSTOP); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci syscall(SYS_gettid, 10, 11, 12, 13, 14, 15); 8362306a36Sopenharmony_ci _exit(0); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci /* Parent process below */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Wait for SIGSTOP sent by tgkill above. */ 8862306a36Sopenharmony_ci if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status)) 8962306a36Sopenharmony_ci err(1, "waitpid"); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci printf("[RUN]\tSYSEMU\n"); 9262306a36Sopenharmony_ci if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) 9362306a36Sopenharmony_ci err(1, "PTRACE_SYSEMU"); 9462306a36Sopenharmony_ci wait_trap(chld); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) 9762306a36Sopenharmony_ci err(1, "PTRACE_GETREGS"); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * Ptrace trapped prior to executing the syscall, thus r3 still has 10162306a36Sopenharmony_ci * the syscall number instead of the sys_gettid() result 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci if (regs.user_syscall_nr != SYS_gettid || 10462306a36Sopenharmony_ci regs.user_arg0 != 10 || regs.user_arg1 != 11 || 10562306a36Sopenharmony_ci regs.user_arg2 != 12 || regs.user_arg3 != 13 || 10662306a36Sopenharmony_ci regs.user_arg4 != 14 || regs.user_arg5 != 15) { 10762306a36Sopenharmony_ci printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", 10862306a36Sopenharmony_ci (unsigned long)regs.user_syscall_nr, 10962306a36Sopenharmony_ci (unsigned long)regs.user_arg0, 11062306a36Sopenharmony_ci (unsigned long)regs.user_arg1, 11162306a36Sopenharmony_ci (unsigned long)regs.user_arg2, 11262306a36Sopenharmony_ci (unsigned long)regs.user_arg3, 11362306a36Sopenharmony_ci (unsigned long)regs.user_arg4, 11462306a36Sopenharmony_ci (unsigned long)regs.user_arg5); 11562306a36Sopenharmony_ci nerrs++; 11662306a36Sopenharmony_ci } else { 11762306a36Sopenharmony_ci printf("[OK]\tInitial nr and args are correct\n"); } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n", 12062306a36Sopenharmony_ci (unsigned long)regs.user_ip); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* 12362306a36Sopenharmony_ci * Rewind to retry the same syscall again. This will basically test 12462306a36Sopenharmony_ci * the rewind process together with PTRACE_SETREGS and PTRACE_GETREGS. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci regs.user_ip -= 4; 12762306a36Sopenharmony_ci if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) 12862306a36Sopenharmony_ci err(1, "PTRACE_SETREGS"); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) 13162306a36Sopenharmony_ci err(1, "PTRACE_SYSEMU"); 13262306a36Sopenharmony_ci wait_trap(chld); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) 13562306a36Sopenharmony_ci err(1, "PTRACE_GETREGS"); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (regs.user_syscall_nr != SYS_gettid || 13862306a36Sopenharmony_ci regs.user_arg0 != 10 || regs.user_arg1 != 11 || 13962306a36Sopenharmony_ci regs.user_arg2 != 12 || regs.user_arg3 != 13 || 14062306a36Sopenharmony_ci regs.user_arg4 != 14 || regs.user_arg5 != 15) { 14162306a36Sopenharmony_ci printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", 14262306a36Sopenharmony_ci (unsigned long)regs.user_syscall_nr, 14362306a36Sopenharmony_ci (unsigned long)regs.user_arg0, 14462306a36Sopenharmony_ci (unsigned long)regs.user_arg1, 14562306a36Sopenharmony_ci (unsigned long)regs.user_arg2, 14662306a36Sopenharmony_ci (unsigned long)regs.user_arg3, 14762306a36Sopenharmony_ci (unsigned long)regs.user_arg4, 14862306a36Sopenharmony_ci (unsigned long)regs.user_arg5); 14962306a36Sopenharmony_ci nerrs++; 15062306a36Sopenharmony_ci } else { 15162306a36Sopenharmony_ci printf("[OK]\tRestarted nr and args are correct\n"); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n", 15562306a36Sopenharmony_ci (unsigned long)regs.user_ip); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* 15862306a36Sopenharmony_ci * Inject a new syscall (getpid) in the same place the previous 15962306a36Sopenharmony_ci * syscall (gettid), rewind and re-execute. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci regs.user_syscall_nr = SYS_getpid; 16262306a36Sopenharmony_ci regs.user_arg0 = 20; 16362306a36Sopenharmony_ci regs.user_arg1 = 21; 16462306a36Sopenharmony_ci regs.user_arg2 = 22; 16562306a36Sopenharmony_ci regs.user_arg3 = 23; 16662306a36Sopenharmony_ci regs.user_arg4 = 24; 16762306a36Sopenharmony_ci regs.user_arg5 = 25; 16862306a36Sopenharmony_ci regs.user_ip -= 4; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) 17162306a36Sopenharmony_ci err(1, "PTRACE_SETREGS"); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) 17462306a36Sopenharmony_ci err(1, "PTRACE_SYSEMU"); 17562306a36Sopenharmony_ci wait_trap(chld); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) 17862306a36Sopenharmony_ci err(1, "PTRACE_GETREGS"); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Check that ptrace stopped at the new syscall that was 18162306a36Sopenharmony_ci * injected, and guarantee that it haven't executed, i.e, user_args 18262306a36Sopenharmony_ci * contain the arguments and not the syscall return value, for 18362306a36Sopenharmony_ci * instance. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ci if (regs.user_syscall_nr != SYS_getpid 18662306a36Sopenharmony_ci || regs.user_arg0 != 20 || regs.user_arg1 != 21 18762306a36Sopenharmony_ci || regs.user_arg2 != 22 || regs.user_arg3 != 23 18862306a36Sopenharmony_ci || regs.user_arg4 != 24 || regs.user_arg5 != 25) { 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", 19162306a36Sopenharmony_ci (unsigned long)regs.user_syscall_nr, 19262306a36Sopenharmony_ci (unsigned long)regs.user_arg0, 19362306a36Sopenharmony_ci (unsigned long)regs.user_arg1, 19462306a36Sopenharmony_ci (unsigned long)regs.user_arg2, 19562306a36Sopenharmony_ci (unsigned long)regs.user_arg3, 19662306a36Sopenharmony_ci (unsigned long)regs.user_arg4, 19762306a36Sopenharmony_ci (unsigned long)regs.user_arg5); 19862306a36Sopenharmony_ci nerrs++; 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci printf("[OK]\tReplacement nr and args are correct\n"); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (ptrace(PTRACE_CONT, chld, 0, 0) != 0) 20462306a36Sopenharmony_ci err(1, "PTRACE_CONT"); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (waitpid(chld, &status, 0) != chld) 20762306a36Sopenharmony_ci err(1, "waitpid"); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* Guarantee that the process executed properly, returning 0 */ 21062306a36Sopenharmony_ci if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 21162306a36Sopenharmony_ci printf("[FAIL]\tChild failed\n"); 21262306a36Sopenharmony_ci nerrs++; 21362306a36Sopenharmony_ci } else { 21462306a36Sopenharmony_ci printf("[OK]\tChild exited cleanly\n"); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciint ptrace_syscall(void) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci test_ptrace_syscall_restart(); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return nerrs; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ciint main(void) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci return test_harness(ptrace_syscall, "ptrace_syscall"); 22862306a36Sopenharmony_ci} 229