18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * A ptrace test for testing PTRACE_SYSEMU, PTRACE_SETREGS and 48c2ecf20Sopenharmony_ci * PTRACE_GETREG. This test basically create a child process that executes 58c2ecf20Sopenharmony_ci * syscalls and the parent process check if it is being traced appropriated. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This test is heavily based on tools/testing/selftests/x86/ptrace_syscall.c 88c2ecf20Sopenharmony_ci * test, and it was adapted to run on Powerpc by 98c2ecf20Sopenharmony_ci * Breno Leitao <leitao@debian.org> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#define _GNU_SOURCE 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <sys/ptrace.h> 148c2ecf20Sopenharmony_ci#include <sys/types.h> 158c2ecf20Sopenharmony_ci#include <sys/wait.h> 168c2ecf20Sopenharmony_ci#include <sys/syscall.h> 178c2ecf20Sopenharmony_ci#include <sys/user.h> 188c2ecf20Sopenharmony_ci#include <unistd.h> 198c2ecf20Sopenharmony_ci#include <errno.h> 208c2ecf20Sopenharmony_ci#include <stddef.h> 218c2ecf20Sopenharmony_ci#include <stdio.h> 228c2ecf20Sopenharmony_ci#include <err.h> 238c2ecf20Sopenharmony_ci#include <string.h> 248c2ecf20Sopenharmony_ci#include <sys/auxv.h> 258c2ecf20Sopenharmony_ci#include "utils.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* Bitness-agnostic defines for user_regs_struct fields. */ 288c2ecf20Sopenharmony_ci#define user_syscall_nr gpr[0] 298c2ecf20Sopenharmony_ci#define user_arg0 gpr[3] 308c2ecf20Sopenharmony_ci#define user_arg1 gpr[4] 318c2ecf20Sopenharmony_ci#define user_arg2 gpr[5] 328c2ecf20Sopenharmony_ci#define user_arg3 gpr[6] 338c2ecf20Sopenharmony_ci#define user_arg4 gpr[7] 348c2ecf20Sopenharmony_ci#define user_arg5 gpr[8] 358c2ecf20Sopenharmony_ci#define user_ip nip 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define PTRACE_SYSEMU 0x1d 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int nerrs; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void wait_trap(pid_t chld) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci siginfo_t si; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0) 468c2ecf20Sopenharmony_ci err(1, "waitid"); 478c2ecf20Sopenharmony_ci if (si.si_pid != chld) 488c2ecf20Sopenharmony_ci errx(1, "got unexpected pid in event\n"); 498c2ecf20Sopenharmony_ci if (si.si_code != CLD_TRAPPED) 508c2ecf20Sopenharmony_ci errx(1, "got unexpected event type %d\n", si.si_code); 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void test_ptrace_syscall_restart(void) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int status; 568c2ecf20Sopenharmony_ci struct pt_regs regs; 578c2ecf20Sopenharmony_ci pid_t chld; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci printf("[RUN]\tptrace-induced syscall restart\n"); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci chld = fork(); 628c2ecf20Sopenharmony_ci if (chld < 0) 638c2ecf20Sopenharmony_ci err(1, "fork"); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* 668c2ecf20Sopenharmony_ci * Child process is running 4 syscalls after ptrace. 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * 1) getpid() 698c2ecf20Sopenharmony_ci * 2) gettid() 708c2ecf20Sopenharmony_ci * 3) tgkill() -> Send SIGSTOP 718c2ecf20Sopenharmony_ci * 4) gettid() -> Where the tests will happen essentially 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci if (chld == 0) { 748c2ecf20Sopenharmony_ci if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0) 758c2ecf20Sopenharmony_ci err(1, "PTRACE_TRACEME"); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci pid_t pid = getpid(), tid = syscall(SYS_gettid); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci printf("\tChild will make one syscall\n"); 808c2ecf20Sopenharmony_ci syscall(SYS_tgkill, pid, tid, SIGSTOP); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci syscall(SYS_gettid, 10, 11, 12, 13, 14, 15); 838c2ecf20Sopenharmony_ci _exit(0); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci /* Parent process below */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Wait for SIGSTOP sent by tgkill above. */ 888c2ecf20Sopenharmony_ci if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status)) 898c2ecf20Sopenharmony_ci err(1, "waitpid"); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci printf("[RUN]\tSYSEMU\n"); 928c2ecf20Sopenharmony_ci if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) 938c2ecf20Sopenharmony_ci err(1, "PTRACE_SYSEMU"); 948c2ecf20Sopenharmony_ci wait_trap(chld); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) 978c2ecf20Sopenharmony_ci err(1, "PTRACE_GETREGS"); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * Ptrace trapped prior to executing the syscall, thus r3 still has 1018c2ecf20Sopenharmony_ci * the syscall number instead of the sys_gettid() result 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci if (regs.user_syscall_nr != SYS_gettid || 1048c2ecf20Sopenharmony_ci regs.user_arg0 != 10 || regs.user_arg1 != 11 || 1058c2ecf20Sopenharmony_ci regs.user_arg2 != 12 || regs.user_arg3 != 13 || 1068c2ecf20Sopenharmony_ci regs.user_arg4 != 14 || regs.user_arg5 != 15) { 1078c2ecf20Sopenharmony_ci printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", 1088c2ecf20Sopenharmony_ci (unsigned long)regs.user_syscall_nr, 1098c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg0, 1108c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg1, 1118c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg2, 1128c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg3, 1138c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg4, 1148c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg5); 1158c2ecf20Sopenharmony_ci nerrs++; 1168c2ecf20Sopenharmony_ci } else { 1178c2ecf20Sopenharmony_ci printf("[OK]\tInitial nr and args are correct\n"); } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n", 1208c2ecf20Sopenharmony_ci (unsigned long)regs.user_ip); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* 1238c2ecf20Sopenharmony_ci * Rewind to retry the same syscall again. This will basically test 1248c2ecf20Sopenharmony_ci * the rewind process together with PTRACE_SETREGS and PTRACE_GETREGS. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci regs.user_ip -= 4; 1278c2ecf20Sopenharmony_ci if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) 1288c2ecf20Sopenharmony_ci err(1, "PTRACE_SETREGS"); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) 1318c2ecf20Sopenharmony_ci err(1, "PTRACE_SYSEMU"); 1328c2ecf20Sopenharmony_ci wait_trap(chld); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) 1358c2ecf20Sopenharmony_ci err(1, "PTRACE_GETREGS"); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (regs.user_syscall_nr != SYS_gettid || 1388c2ecf20Sopenharmony_ci regs.user_arg0 != 10 || regs.user_arg1 != 11 || 1398c2ecf20Sopenharmony_ci regs.user_arg2 != 12 || regs.user_arg3 != 13 || 1408c2ecf20Sopenharmony_ci regs.user_arg4 != 14 || regs.user_arg5 != 15) { 1418c2ecf20Sopenharmony_ci printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", 1428c2ecf20Sopenharmony_ci (unsigned long)regs.user_syscall_nr, 1438c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg0, 1448c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg1, 1458c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg2, 1468c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg3, 1478c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg4, 1488c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg5); 1498c2ecf20Sopenharmony_ci nerrs++; 1508c2ecf20Sopenharmony_ci } else { 1518c2ecf20Sopenharmony_ci printf("[OK]\tRestarted nr and args are correct\n"); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n", 1558c2ecf20Sopenharmony_ci (unsigned long)regs.user_ip); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Inject a new syscall (getpid) in the same place the previous 1598c2ecf20Sopenharmony_ci * syscall (gettid), rewind and re-execute. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ci regs.user_syscall_nr = SYS_getpid; 1628c2ecf20Sopenharmony_ci regs.user_arg0 = 20; 1638c2ecf20Sopenharmony_ci regs.user_arg1 = 21; 1648c2ecf20Sopenharmony_ci regs.user_arg2 = 22; 1658c2ecf20Sopenharmony_ci regs.user_arg3 = 23; 1668c2ecf20Sopenharmony_ci regs.user_arg4 = 24; 1678c2ecf20Sopenharmony_ci regs.user_arg5 = 25; 1688c2ecf20Sopenharmony_ci regs.user_ip -= 4; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (ptrace(PTRACE_SETREGS, chld, 0, ®s) != 0) 1718c2ecf20Sopenharmony_ci err(1, "PTRACE_SETREGS"); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0) 1748c2ecf20Sopenharmony_ci err(1, "PTRACE_SYSEMU"); 1758c2ecf20Sopenharmony_ci wait_trap(chld); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (ptrace(PTRACE_GETREGS, chld, 0, ®s) != 0) 1788c2ecf20Sopenharmony_ci err(1, "PTRACE_GETREGS"); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Check that ptrace stopped at the new syscall that was 1818c2ecf20Sopenharmony_ci * injected, and guarantee that it haven't executed, i.e, user_args 1828c2ecf20Sopenharmony_ci * contain the arguments and not the syscall return value, for 1838c2ecf20Sopenharmony_ci * instance. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci if (regs.user_syscall_nr != SYS_getpid 1868c2ecf20Sopenharmony_ci || regs.user_arg0 != 20 || regs.user_arg1 != 21 1878c2ecf20Sopenharmony_ci || regs.user_arg2 != 22 || regs.user_arg3 != 23 1888c2ecf20Sopenharmony_ci || regs.user_arg4 != 24 || regs.user_arg5 != 25) { 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", 1918c2ecf20Sopenharmony_ci (unsigned long)regs.user_syscall_nr, 1928c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg0, 1938c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg1, 1948c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg2, 1958c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg3, 1968c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg4, 1978c2ecf20Sopenharmony_ci (unsigned long)regs.user_arg5); 1988c2ecf20Sopenharmony_ci nerrs++; 1998c2ecf20Sopenharmony_ci } else { 2008c2ecf20Sopenharmony_ci printf("[OK]\tReplacement nr and args are correct\n"); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (ptrace(PTRACE_CONT, chld, 0, 0) != 0) 2048c2ecf20Sopenharmony_ci err(1, "PTRACE_CONT"); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (waitpid(chld, &status, 0) != chld) 2078c2ecf20Sopenharmony_ci err(1, "waitpid"); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* Guarantee that the process executed properly, returning 0 */ 2108c2ecf20Sopenharmony_ci if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 2118c2ecf20Sopenharmony_ci printf("[FAIL]\tChild failed\n"); 2128c2ecf20Sopenharmony_ci nerrs++; 2138c2ecf20Sopenharmony_ci } else { 2148c2ecf20Sopenharmony_ci printf("[OK]\tChild exited cleanly\n"); 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ciint ptrace_syscall(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci test_ptrace_syscall_restart(); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return nerrs; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ciint main(void) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci return test_harness(ptrace_syscall, "ptrace_syscall"); 2288c2ecf20Sopenharmony_ci} 229