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, &regs) != 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, &regs) != 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, &regs) != 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, &regs) != 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, &regs) != 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