xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/powerpc/ptrace/ptrace-syscall.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * A ptrace test for testing PTRACE_SYSEMU, PTRACE_SETREGS and
4*4882a593Smuzhiyun  * PTRACE_GETREG.  This test basically create a child process that executes
5*4882a593Smuzhiyun  * syscalls and the parent process check if it is being traced appropriated.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * This test is heavily based on tools/testing/selftests/x86/ptrace_syscall.c
8*4882a593Smuzhiyun  * test, and it was adapted to run on Powerpc by
9*4882a593Smuzhiyun  * Breno Leitao <leitao@debian.org>
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun #define _GNU_SOURCE
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun #include <sys/ptrace.h>
14*4882a593Smuzhiyun #include <sys/types.h>
15*4882a593Smuzhiyun #include <sys/wait.h>
16*4882a593Smuzhiyun #include <sys/syscall.h>
17*4882a593Smuzhiyun #include <sys/user.h>
18*4882a593Smuzhiyun #include <unistd.h>
19*4882a593Smuzhiyun #include <errno.h>
20*4882a593Smuzhiyun #include <stddef.h>
21*4882a593Smuzhiyun #include <stdio.h>
22*4882a593Smuzhiyun #include <err.h>
23*4882a593Smuzhiyun #include <string.h>
24*4882a593Smuzhiyun #include <sys/auxv.h>
25*4882a593Smuzhiyun #include "utils.h"
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun /* Bitness-agnostic defines for user_regs_struct fields. */
28*4882a593Smuzhiyun #define user_syscall_nr	gpr[0]
29*4882a593Smuzhiyun #define user_arg0		gpr[3]
30*4882a593Smuzhiyun #define user_arg1		gpr[4]
31*4882a593Smuzhiyun #define user_arg2		gpr[5]
32*4882a593Smuzhiyun #define user_arg3		gpr[6]
33*4882a593Smuzhiyun #define user_arg4		gpr[7]
34*4882a593Smuzhiyun #define user_arg5		gpr[8]
35*4882a593Smuzhiyun #define user_ip		nip
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #define PTRACE_SYSEMU		0x1d
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun static int nerrs;
40*4882a593Smuzhiyun 
wait_trap(pid_t chld)41*4882a593Smuzhiyun static void wait_trap(pid_t chld)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun 	siginfo_t si;
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun 	if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0)
46*4882a593Smuzhiyun 		err(1, "waitid");
47*4882a593Smuzhiyun 	if (si.si_pid != chld)
48*4882a593Smuzhiyun 		errx(1, "got unexpected pid in event\n");
49*4882a593Smuzhiyun 	if (si.si_code != CLD_TRAPPED)
50*4882a593Smuzhiyun 		errx(1, "got unexpected event type %d\n", si.si_code);
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
test_ptrace_syscall_restart(void)53*4882a593Smuzhiyun static void test_ptrace_syscall_restart(void)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun 	int status;
56*4882a593Smuzhiyun 	struct pt_regs regs;
57*4882a593Smuzhiyun 	pid_t chld;
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	printf("[RUN]\tptrace-induced syscall restart\n");
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	chld = fork();
62*4882a593Smuzhiyun 	if (chld < 0)
63*4882a593Smuzhiyun 		err(1, "fork");
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	/*
66*4882a593Smuzhiyun 	 * Child process is running 4 syscalls after ptrace.
67*4882a593Smuzhiyun 	 *
68*4882a593Smuzhiyun 	 * 1) getpid()
69*4882a593Smuzhiyun 	 * 2) gettid()
70*4882a593Smuzhiyun 	 * 3) tgkill() -> Send SIGSTOP
71*4882a593Smuzhiyun 	 * 4) gettid() -> Where the tests will happen essentially
72*4882a593Smuzhiyun 	 */
73*4882a593Smuzhiyun 	if (chld == 0) {
74*4882a593Smuzhiyun 		if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
75*4882a593Smuzhiyun 			err(1, "PTRACE_TRACEME");
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 		pid_t pid = getpid(), tid = syscall(SYS_gettid);
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 		printf("\tChild will make one syscall\n");
80*4882a593Smuzhiyun 		syscall(SYS_tgkill, pid, tid, SIGSTOP);
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 		syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
83*4882a593Smuzhiyun 		_exit(0);
84*4882a593Smuzhiyun 	}
85*4882a593Smuzhiyun 	/* Parent process below */
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	/* Wait for SIGSTOP sent by tgkill above. */
88*4882a593Smuzhiyun 	if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
89*4882a593Smuzhiyun 		err(1, "waitpid");
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	printf("[RUN]\tSYSEMU\n");
92*4882a593Smuzhiyun 	if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
93*4882a593Smuzhiyun 		err(1, "PTRACE_SYSEMU");
94*4882a593Smuzhiyun 	wait_trap(chld);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
97*4882a593Smuzhiyun 		err(1, "PTRACE_GETREGS");
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	/*
100*4882a593Smuzhiyun 	 * Ptrace trapped prior to executing the syscall, thus r3 still has
101*4882a593Smuzhiyun 	 * the syscall number instead of the sys_gettid() result
102*4882a593Smuzhiyun 	 */
103*4882a593Smuzhiyun 	if (regs.user_syscall_nr != SYS_gettid ||
104*4882a593Smuzhiyun 	    regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
105*4882a593Smuzhiyun 	    regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
106*4882a593Smuzhiyun 	    regs.user_arg4 != 14 || regs.user_arg5 != 15) {
107*4882a593Smuzhiyun 		printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
108*4882a593Smuzhiyun 			(unsigned long)regs.user_syscall_nr,
109*4882a593Smuzhiyun 			(unsigned long)regs.user_arg0,
110*4882a593Smuzhiyun 			(unsigned long)regs.user_arg1,
111*4882a593Smuzhiyun 			(unsigned long)regs.user_arg2,
112*4882a593Smuzhiyun 			(unsigned long)regs.user_arg3,
113*4882a593Smuzhiyun 			(unsigned long)regs.user_arg4,
114*4882a593Smuzhiyun 			(unsigned long)regs.user_arg5);
115*4882a593Smuzhiyun 		 nerrs++;
116*4882a593Smuzhiyun 	} else {
117*4882a593Smuzhiyun 		printf("[OK]\tInitial nr and args are correct\n"); }
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n",
120*4882a593Smuzhiyun 	       (unsigned long)regs.user_ip);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	/*
123*4882a593Smuzhiyun 	 * Rewind to retry the same syscall again. This will basically test
124*4882a593Smuzhiyun 	 * the rewind process together with PTRACE_SETREGS and PTRACE_GETREGS.
125*4882a593Smuzhiyun 	 */
126*4882a593Smuzhiyun 	regs.user_ip -= 4;
127*4882a593Smuzhiyun 	if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
128*4882a593Smuzhiyun 		err(1, "PTRACE_SETREGS");
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
131*4882a593Smuzhiyun 		err(1, "PTRACE_SYSEMU");
132*4882a593Smuzhiyun 	wait_trap(chld);
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
135*4882a593Smuzhiyun 		err(1, "PTRACE_GETREGS");
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	if (regs.user_syscall_nr != SYS_gettid ||
138*4882a593Smuzhiyun 	    regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
139*4882a593Smuzhiyun 	    regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
140*4882a593Smuzhiyun 	    regs.user_arg4 != 14 || regs.user_arg5 != 15) {
141*4882a593Smuzhiyun 		printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
142*4882a593Smuzhiyun 			(unsigned long)regs.user_syscall_nr,
143*4882a593Smuzhiyun 			(unsigned long)regs.user_arg0,
144*4882a593Smuzhiyun 			(unsigned long)regs.user_arg1,
145*4882a593Smuzhiyun 			(unsigned long)regs.user_arg2,
146*4882a593Smuzhiyun 			(unsigned long)regs.user_arg3,
147*4882a593Smuzhiyun 			(unsigned long)regs.user_arg4,
148*4882a593Smuzhiyun 			(unsigned long)regs.user_arg5);
149*4882a593Smuzhiyun 		nerrs++;
150*4882a593Smuzhiyun 	} else {
151*4882a593Smuzhiyun 		printf("[OK]\tRestarted nr and args are correct\n");
152*4882a593Smuzhiyun 	}
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n",
155*4882a593Smuzhiyun 	       (unsigned long)regs.user_ip);
156*4882a593Smuzhiyun 
157*4882a593Smuzhiyun 	/*
158*4882a593Smuzhiyun 	 * Inject a new syscall (getpid) in the same place the previous
159*4882a593Smuzhiyun 	 * syscall (gettid), rewind and re-execute.
160*4882a593Smuzhiyun 	 */
161*4882a593Smuzhiyun 	regs.user_syscall_nr = SYS_getpid;
162*4882a593Smuzhiyun 	regs.user_arg0 = 20;
163*4882a593Smuzhiyun 	regs.user_arg1 = 21;
164*4882a593Smuzhiyun 	regs.user_arg2 = 22;
165*4882a593Smuzhiyun 	regs.user_arg3 = 23;
166*4882a593Smuzhiyun 	regs.user_arg4 = 24;
167*4882a593Smuzhiyun 	regs.user_arg5 = 25;
168*4882a593Smuzhiyun 	regs.user_ip -= 4;
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
171*4882a593Smuzhiyun 		err(1, "PTRACE_SETREGS");
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
174*4882a593Smuzhiyun 		err(1, "PTRACE_SYSEMU");
175*4882a593Smuzhiyun 	wait_trap(chld);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
178*4882a593Smuzhiyun 		err(1, "PTRACE_GETREGS");
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	/* Check that ptrace stopped at the new syscall that was
181*4882a593Smuzhiyun 	 * injected, and guarantee that it haven't executed, i.e, user_args
182*4882a593Smuzhiyun 	 * contain the arguments and not the syscall return value, for
183*4882a593Smuzhiyun 	 * instance.
184*4882a593Smuzhiyun 	 */
185*4882a593Smuzhiyun 	if (regs.user_syscall_nr != SYS_getpid
186*4882a593Smuzhiyun 		|| regs.user_arg0 != 20 || regs.user_arg1 != 21
187*4882a593Smuzhiyun 		|| regs.user_arg2 != 22 || regs.user_arg3 != 23
188*4882a593Smuzhiyun 		|| regs.user_arg4 != 24 || regs.user_arg5 != 25) {
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun 		printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n",
191*4882a593Smuzhiyun 			(unsigned long)regs.user_syscall_nr,
192*4882a593Smuzhiyun 			(unsigned long)regs.user_arg0,
193*4882a593Smuzhiyun 			(unsigned long)regs.user_arg1,
194*4882a593Smuzhiyun 			(unsigned long)regs.user_arg2,
195*4882a593Smuzhiyun 			(unsigned long)regs.user_arg3,
196*4882a593Smuzhiyun 			(unsigned long)regs.user_arg4,
197*4882a593Smuzhiyun 			(unsigned long)regs.user_arg5);
198*4882a593Smuzhiyun 		nerrs++;
199*4882a593Smuzhiyun 	} else {
200*4882a593Smuzhiyun 		printf("[OK]\tReplacement nr and args are correct\n");
201*4882a593Smuzhiyun 	}
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
204*4882a593Smuzhiyun 		err(1, "PTRACE_CONT");
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	if (waitpid(chld, &status, 0) != chld)
207*4882a593Smuzhiyun 		err(1, "waitpid");
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	/* Guarantee that the process executed properly, returning 0 */
210*4882a593Smuzhiyun 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
211*4882a593Smuzhiyun 		printf("[FAIL]\tChild failed\n");
212*4882a593Smuzhiyun 		nerrs++;
213*4882a593Smuzhiyun 	} else {
214*4882a593Smuzhiyun 		printf("[OK]\tChild exited cleanly\n");
215*4882a593Smuzhiyun 	}
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun 
ptrace_syscall(void)218*4882a593Smuzhiyun int ptrace_syscall(void)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun 	test_ptrace_syscall_restart();
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	return nerrs;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun 
main(void)225*4882a593Smuzhiyun int main(void)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun 	return test_harness(ptrace_syscall, "ptrace_syscall");
228*4882a593Smuzhiyun }
229