xref: /linux/tools/testing/selftests/ptrace/set_syscall_info.c (revision 00c010e130e58301db2ea0cec1eadc931e1cb8cf)
1*bc6fa711SDmitry V. Levin // SPDX-License-Identifier: GPL-2.0+
2*bc6fa711SDmitry V. Levin /*
3*bc6fa711SDmitry V. Levin  * Copyright (c) 2018-2025 Dmitry V. Levin <ldv@strace.io>
4*bc6fa711SDmitry V. Levin  * All rights reserved.
5*bc6fa711SDmitry V. Levin  *
6*bc6fa711SDmitry V. Levin  * Check whether PTRACE_SET_SYSCALL_INFO semantics implemented in the kernel
7*bc6fa711SDmitry V. Levin  * matches userspace expectations.
8*bc6fa711SDmitry V. Levin  */
9*bc6fa711SDmitry V. Levin 
10*bc6fa711SDmitry V. Levin #include "../kselftest_harness.h"
11*bc6fa711SDmitry V. Levin #include <err.h>
12*bc6fa711SDmitry V. Levin #include <fcntl.h>
13*bc6fa711SDmitry V. Levin #include <signal.h>
14*bc6fa711SDmitry V. Levin #include <asm/unistd.h>
15*bc6fa711SDmitry V. Levin #include <linux/types.h>
16*bc6fa711SDmitry V. Levin #include <linux/ptrace.h>
17*bc6fa711SDmitry V. Levin 
18*bc6fa711SDmitry V. Levin #if defined(_MIPS_SIM) && _MIPS_SIM == _MIPS_SIM_NABI32
19*bc6fa711SDmitry V. Levin /*
20*bc6fa711SDmitry V. Levin  * MIPS N32 is the only architecture where __kernel_ulong_t
21*bc6fa711SDmitry V. Levin  * does not match the bitness of syscall arguments.
22*bc6fa711SDmitry V. Levin  */
23*bc6fa711SDmitry V. Levin typedef unsigned long long kernel_ulong_t;
24*bc6fa711SDmitry V. Levin #else
25*bc6fa711SDmitry V. Levin typedef __kernel_ulong_t kernel_ulong_t;
26*bc6fa711SDmitry V. Levin #endif
27*bc6fa711SDmitry V. Levin 
28*bc6fa711SDmitry V. Levin struct si_entry {
29*bc6fa711SDmitry V. Levin 	int nr;
30*bc6fa711SDmitry V. Levin 	kernel_ulong_t args[6];
31*bc6fa711SDmitry V. Levin };
32*bc6fa711SDmitry V. Levin struct si_exit {
33*bc6fa711SDmitry V. Levin 	unsigned int is_error;
34*bc6fa711SDmitry V. Levin 	int rval;
35*bc6fa711SDmitry V. Levin };
36*bc6fa711SDmitry V. Levin 
37*bc6fa711SDmitry V. Levin static unsigned int ptrace_stop;
38*bc6fa711SDmitry V. Levin static pid_t tracee_pid;
39*bc6fa711SDmitry V. Levin 
40*bc6fa711SDmitry V. Levin static int
kill_tracee(pid_t pid)41*bc6fa711SDmitry V. Levin kill_tracee(pid_t pid)
42*bc6fa711SDmitry V. Levin {
43*bc6fa711SDmitry V. Levin 	if (!pid)
44*bc6fa711SDmitry V. Levin 		return 0;
45*bc6fa711SDmitry V. Levin 
46*bc6fa711SDmitry V. Levin 	int saved_errno = errno;
47*bc6fa711SDmitry V. Levin 
48*bc6fa711SDmitry V. Levin 	int rc = kill(pid, SIGKILL);
49*bc6fa711SDmitry V. Levin 
50*bc6fa711SDmitry V. Levin 	errno = saved_errno;
51*bc6fa711SDmitry V. Levin 	return rc;
52*bc6fa711SDmitry V. Levin }
53*bc6fa711SDmitry V. Levin 
54*bc6fa711SDmitry V. Levin static long
sys_ptrace(int request,pid_t pid,unsigned long addr,unsigned long data)55*bc6fa711SDmitry V. Levin sys_ptrace(int request, pid_t pid, unsigned long addr, unsigned long data)
56*bc6fa711SDmitry V. Levin {
57*bc6fa711SDmitry V. Levin 	return syscall(__NR_ptrace, request, pid, addr, data);
58*bc6fa711SDmitry V. Levin }
59*bc6fa711SDmitry V. Levin 
60*bc6fa711SDmitry V. Levin #define LOG_KILL_TRACEE(fmt, ...)				\
61*bc6fa711SDmitry V. Levin 	do {							\
62*bc6fa711SDmitry V. Levin 		kill_tracee(tracee_pid);			\
63*bc6fa711SDmitry V. Levin 		TH_LOG("wait #%d: " fmt,			\
64*bc6fa711SDmitry V. Levin 		       ptrace_stop, ##__VA_ARGS__);		\
65*bc6fa711SDmitry V. Levin 	} while (0)
66*bc6fa711SDmitry V. Levin 
67*bc6fa711SDmitry V. Levin static void
check_psi_entry(struct __test_metadata * _metadata,const struct ptrace_syscall_info * info,const struct si_entry * exp_entry,const char * text)68*bc6fa711SDmitry V. Levin check_psi_entry(struct __test_metadata *_metadata,
69*bc6fa711SDmitry V. Levin 		const struct ptrace_syscall_info *info,
70*bc6fa711SDmitry V. Levin 		const struct si_entry *exp_entry,
71*bc6fa711SDmitry V. Levin 		const char *text)
72*bc6fa711SDmitry V. Levin {
73*bc6fa711SDmitry V. Levin 	unsigned int i;
74*bc6fa711SDmitry V. Levin 	int exp_nr = exp_entry->nr;
75*bc6fa711SDmitry V. Levin #if defined __s390__ || defined __s390x__
76*bc6fa711SDmitry V. Levin 	/* s390 is the only architecture that has 16-bit syscall numbers */
77*bc6fa711SDmitry V. Levin 	exp_nr &= 0xffff;
78*bc6fa711SDmitry V. Levin #endif
79*bc6fa711SDmitry V. Levin 
80*bc6fa711SDmitry V. Levin 	ASSERT_EQ(PTRACE_SYSCALL_INFO_ENTRY, info->op) {
81*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: entry stop mismatch", text);
82*bc6fa711SDmitry V. Levin 	}
83*bc6fa711SDmitry V. Levin 	ASSERT_TRUE(info->arch) {
84*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: entry stop mismatch", text);
85*bc6fa711SDmitry V. Levin 	}
86*bc6fa711SDmitry V. Levin 	ASSERT_TRUE(info->instruction_pointer) {
87*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: entry stop mismatch", text);
88*bc6fa711SDmitry V. Levin 	}
89*bc6fa711SDmitry V. Levin 	ASSERT_TRUE(info->stack_pointer) {
90*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: entry stop mismatch", text);
91*bc6fa711SDmitry V. Levin 	}
92*bc6fa711SDmitry V. Levin 	ASSERT_EQ(exp_nr, info->entry.nr) {
93*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: syscall nr mismatch", text);
94*bc6fa711SDmitry V. Levin 	}
95*bc6fa711SDmitry V. Levin 	for (i = 0; i < ARRAY_SIZE(exp_entry->args); ++i) {
96*bc6fa711SDmitry V. Levin 		ASSERT_EQ(exp_entry->args[i], info->entry.args[i]) {
97*bc6fa711SDmitry V. Levin 			LOG_KILL_TRACEE("%s: syscall arg #%u mismatch",
98*bc6fa711SDmitry V. Levin 					text, i);
99*bc6fa711SDmitry V. Levin 		}
100*bc6fa711SDmitry V. Levin 	}
101*bc6fa711SDmitry V. Levin }
102*bc6fa711SDmitry V. Levin 
103*bc6fa711SDmitry V. Levin static void
check_psi_exit(struct __test_metadata * _metadata,const struct ptrace_syscall_info * info,const struct si_exit * exp_exit,const char * text)104*bc6fa711SDmitry V. Levin check_psi_exit(struct __test_metadata *_metadata,
105*bc6fa711SDmitry V. Levin 	       const struct ptrace_syscall_info *info,
106*bc6fa711SDmitry V. Levin 	       const struct si_exit *exp_exit,
107*bc6fa711SDmitry V. Levin 	       const char *text)
108*bc6fa711SDmitry V. Levin {
109*bc6fa711SDmitry V. Levin 	ASSERT_EQ(PTRACE_SYSCALL_INFO_EXIT, info->op) {
110*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: exit stop mismatch", text);
111*bc6fa711SDmitry V. Levin 	}
112*bc6fa711SDmitry V. Levin 	ASSERT_TRUE(info->arch) {
113*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: exit stop mismatch", text);
114*bc6fa711SDmitry V. Levin 	}
115*bc6fa711SDmitry V. Levin 	ASSERT_TRUE(info->instruction_pointer) {
116*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: exit stop mismatch", text);
117*bc6fa711SDmitry V. Levin 	}
118*bc6fa711SDmitry V. Levin 	ASSERT_TRUE(info->stack_pointer) {
119*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: exit stop mismatch", text);
120*bc6fa711SDmitry V. Levin 	}
121*bc6fa711SDmitry V. Levin 	ASSERT_EQ(exp_exit->is_error, info->exit.is_error) {
122*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: exit stop mismatch", text);
123*bc6fa711SDmitry V. Levin 	}
124*bc6fa711SDmitry V. Levin 	ASSERT_EQ(exp_exit->rval, info->exit.rval) {
125*bc6fa711SDmitry V. Levin 		LOG_KILL_TRACEE("%s: exit stop mismatch", text);
126*bc6fa711SDmitry V. Levin 	}
127*bc6fa711SDmitry V. Levin }
128*bc6fa711SDmitry V. Levin 
TEST(set_syscall_info)129*bc6fa711SDmitry V. Levin TEST(set_syscall_info)
130*bc6fa711SDmitry V. Levin {
131*bc6fa711SDmitry V. Levin 	const pid_t tracer_pid = getpid();
132*bc6fa711SDmitry V. Levin 	const kernel_ulong_t dummy[] = {
133*bc6fa711SDmitry V. Levin 		(kernel_ulong_t) 0xdad0bef0bad0fed0ULL,
134*bc6fa711SDmitry V. Levin 		(kernel_ulong_t) 0xdad1bef1bad1fed1ULL,
135*bc6fa711SDmitry V. Levin 		(kernel_ulong_t) 0xdad2bef2bad2fed2ULL,
136*bc6fa711SDmitry V. Levin 		(kernel_ulong_t) 0xdad3bef3bad3fed3ULL,
137*bc6fa711SDmitry V. Levin 		(kernel_ulong_t) 0xdad4bef4bad4fed4ULL,
138*bc6fa711SDmitry V. Levin 		(kernel_ulong_t) 0xdad5bef5bad5fed5ULL,
139*bc6fa711SDmitry V. Levin 	};
140*bc6fa711SDmitry V. Levin 	int splice_in[2], splice_out[2];
141*bc6fa711SDmitry V. Levin 
142*bc6fa711SDmitry V. Levin 	ASSERT_EQ(0, pipe(splice_in));
143*bc6fa711SDmitry V. Levin 	ASSERT_EQ(0, pipe(splice_out));
144*bc6fa711SDmitry V. Levin 	ASSERT_EQ(sizeof(dummy), write(splice_in[1], dummy, sizeof(dummy)));
145*bc6fa711SDmitry V. Levin 
146*bc6fa711SDmitry V. Levin 	const struct {
147*bc6fa711SDmitry V. Levin 		struct si_entry entry[2];
148*bc6fa711SDmitry V. Levin 		struct si_exit exit[2];
149*bc6fa711SDmitry V. Levin 	} si[] = {
150*bc6fa711SDmitry V. Levin 		/* change scno, keep non-error rval */
151*bc6fa711SDmitry V. Levin 		{
152*bc6fa711SDmitry V. Levin 			{
153*bc6fa711SDmitry V. Levin 				{
154*bc6fa711SDmitry V. Levin 					__NR_gettid,
155*bc6fa711SDmitry V. Levin 					{
156*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
157*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
158*bc6fa711SDmitry V. Levin 					}
159*bc6fa711SDmitry V. Levin 				}, {
160*bc6fa711SDmitry V. Levin 					__NR_getppid,
161*bc6fa711SDmitry V. Levin 					{
162*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
163*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
164*bc6fa711SDmitry V. Levin 					}
165*bc6fa711SDmitry V. Levin 				}
166*bc6fa711SDmitry V. Levin 			}, {
167*bc6fa711SDmitry V. Levin 				{ 0, tracer_pid }, { 0, tracer_pid }
168*bc6fa711SDmitry V. Levin 			}
169*bc6fa711SDmitry V. Levin 		},
170*bc6fa711SDmitry V. Levin 
171*bc6fa711SDmitry V. Levin 		/* set scno to -1, keep error rval */
172*bc6fa711SDmitry V. Levin 		{
173*bc6fa711SDmitry V. Levin 			{
174*bc6fa711SDmitry V. Levin 				{
175*bc6fa711SDmitry V. Levin 					__NR_chdir,
176*bc6fa711SDmitry V. Levin 					{
177*bc6fa711SDmitry V. Levin 						(uintptr_t) ".",
178*bc6fa711SDmitry V. Levin 						dummy[1], dummy[2],
179*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
180*bc6fa711SDmitry V. Levin 					}
181*bc6fa711SDmitry V. Levin 				}, {
182*bc6fa711SDmitry V. Levin 					-1,
183*bc6fa711SDmitry V. Levin 					{
184*bc6fa711SDmitry V. Levin 						(uintptr_t) ".",
185*bc6fa711SDmitry V. Levin 						dummy[1], dummy[2],
186*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
187*bc6fa711SDmitry V. Levin 					}
188*bc6fa711SDmitry V. Levin 				}
189*bc6fa711SDmitry V. Levin 			}, {
190*bc6fa711SDmitry V. Levin 				{ 1, -ENOSYS }, { 1, -ENOSYS }
191*bc6fa711SDmitry V. Levin 			}
192*bc6fa711SDmitry V. Levin 		},
193*bc6fa711SDmitry V. Levin 
194*bc6fa711SDmitry V. Levin 		/* keep scno, change non-error rval */
195*bc6fa711SDmitry V. Levin 		{
196*bc6fa711SDmitry V. Levin 			{
197*bc6fa711SDmitry V. Levin 				{
198*bc6fa711SDmitry V. Levin 					__NR_getppid,
199*bc6fa711SDmitry V. Levin 					{
200*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
201*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
202*bc6fa711SDmitry V. Levin 					}
203*bc6fa711SDmitry V. Levin 				}, {
204*bc6fa711SDmitry V. Levin 					__NR_getppid,
205*bc6fa711SDmitry V. Levin 					{
206*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
207*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
208*bc6fa711SDmitry V. Levin 					}
209*bc6fa711SDmitry V. Levin 				}
210*bc6fa711SDmitry V. Levin 			}, {
211*bc6fa711SDmitry V. Levin 				{ 0, tracer_pid }, { 0, tracer_pid + 1 }
212*bc6fa711SDmitry V. Levin 			}
213*bc6fa711SDmitry V. Levin 		},
214*bc6fa711SDmitry V. Levin 
215*bc6fa711SDmitry V. Levin 		/* change arg1, keep non-error rval */
216*bc6fa711SDmitry V. Levin 		{
217*bc6fa711SDmitry V. Levin 			{
218*bc6fa711SDmitry V. Levin 				{
219*bc6fa711SDmitry V. Levin 					__NR_chdir,
220*bc6fa711SDmitry V. Levin 					{
221*bc6fa711SDmitry V. Levin 						(uintptr_t) "",
222*bc6fa711SDmitry V. Levin 						dummy[1], dummy[2],
223*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
224*bc6fa711SDmitry V. Levin 					}
225*bc6fa711SDmitry V. Levin 				}, {
226*bc6fa711SDmitry V. Levin 					__NR_chdir,
227*bc6fa711SDmitry V. Levin 					{
228*bc6fa711SDmitry V. Levin 						(uintptr_t) ".",
229*bc6fa711SDmitry V. Levin 						dummy[1], dummy[2],
230*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
231*bc6fa711SDmitry V. Levin 					}
232*bc6fa711SDmitry V. Levin 				}
233*bc6fa711SDmitry V. Levin 			}, {
234*bc6fa711SDmitry V. Levin 				{ 0, 0 }, { 0, 0 }
235*bc6fa711SDmitry V. Levin 			}
236*bc6fa711SDmitry V. Levin 		},
237*bc6fa711SDmitry V. Levin 
238*bc6fa711SDmitry V. Levin 		/* set scno to -1, change error rval to non-error */
239*bc6fa711SDmitry V. Levin 		{
240*bc6fa711SDmitry V. Levin 			{
241*bc6fa711SDmitry V. Levin 				{
242*bc6fa711SDmitry V. Levin 					__NR_gettid,
243*bc6fa711SDmitry V. Levin 					{
244*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
245*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
246*bc6fa711SDmitry V. Levin 					}
247*bc6fa711SDmitry V. Levin 				}, {
248*bc6fa711SDmitry V. Levin 					-1,
249*bc6fa711SDmitry V. Levin 					{
250*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
251*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
252*bc6fa711SDmitry V. Levin 					}
253*bc6fa711SDmitry V. Levin 				}
254*bc6fa711SDmitry V. Levin 			}, {
255*bc6fa711SDmitry V. Levin 				{ 1, -ENOSYS }, { 0, tracer_pid }
256*bc6fa711SDmitry V. Levin 			}
257*bc6fa711SDmitry V. Levin 		},
258*bc6fa711SDmitry V. Levin 
259*bc6fa711SDmitry V. Levin 		/* change scno, change non-error rval to error */
260*bc6fa711SDmitry V. Levin 		{
261*bc6fa711SDmitry V. Levin 			{
262*bc6fa711SDmitry V. Levin 				{
263*bc6fa711SDmitry V. Levin 					__NR_chdir,
264*bc6fa711SDmitry V. Levin 					{
265*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
266*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
267*bc6fa711SDmitry V. Levin 					}
268*bc6fa711SDmitry V. Levin 				}, {
269*bc6fa711SDmitry V. Levin 					__NR_getppid,
270*bc6fa711SDmitry V. Levin 					{
271*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
272*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
273*bc6fa711SDmitry V. Levin 					}
274*bc6fa711SDmitry V. Levin 				}
275*bc6fa711SDmitry V. Levin 			}, {
276*bc6fa711SDmitry V. Levin 				{ 0, tracer_pid }, { 1, -EISDIR }
277*bc6fa711SDmitry V. Levin 			}
278*bc6fa711SDmitry V. Levin 		},
279*bc6fa711SDmitry V. Levin 
280*bc6fa711SDmitry V. Levin 		/* change scno and all args, change non-error rval */
281*bc6fa711SDmitry V. Levin 		{
282*bc6fa711SDmitry V. Levin 			{
283*bc6fa711SDmitry V. Levin 				{
284*bc6fa711SDmitry V. Levin 					__NR_gettid,
285*bc6fa711SDmitry V. Levin 					{
286*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
287*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
288*bc6fa711SDmitry V. Levin 					}
289*bc6fa711SDmitry V. Levin 				}, {
290*bc6fa711SDmitry V. Levin 					__NR_splice,
291*bc6fa711SDmitry V. Levin 					{
292*bc6fa711SDmitry V. Levin 						splice_in[0], 0, splice_out[1], 0,
293*bc6fa711SDmitry V. Levin 						sizeof(dummy), SPLICE_F_NONBLOCK
294*bc6fa711SDmitry V. Levin 					}
295*bc6fa711SDmitry V. Levin 				}
296*bc6fa711SDmitry V. Levin 			}, {
297*bc6fa711SDmitry V. Levin 				{ 0, sizeof(dummy) }, { 0, sizeof(dummy) + 1 }
298*bc6fa711SDmitry V. Levin 			}
299*bc6fa711SDmitry V. Levin 		},
300*bc6fa711SDmitry V. Levin 
301*bc6fa711SDmitry V. Levin 		/* change arg1, no exit stop */
302*bc6fa711SDmitry V. Levin 		{
303*bc6fa711SDmitry V. Levin 			{
304*bc6fa711SDmitry V. Levin 				{
305*bc6fa711SDmitry V. Levin 					__NR_exit_group,
306*bc6fa711SDmitry V. Levin 					{
307*bc6fa711SDmitry V. Levin 						dummy[0], dummy[1], dummy[2],
308*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
309*bc6fa711SDmitry V. Levin 					}
310*bc6fa711SDmitry V. Levin 				}, {
311*bc6fa711SDmitry V. Levin 					__NR_exit_group,
312*bc6fa711SDmitry V. Levin 					{
313*bc6fa711SDmitry V. Levin 						0, dummy[1], dummy[2],
314*bc6fa711SDmitry V. Levin 						dummy[3], dummy[4], dummy[5]
315*bc6fa711SDmitry V. Levin 					}
316*bc6fa711SDmitry V. Levin 				}
317*bc6fa711SDmitry V. Levin 			}, {
318*bc6fa711SDmitry V. Levin 				{ 0, 0 }, { 0, 0 }
319*bc6fa711SDmitry V. Levin 			}
320*bc6fa711SDmitry V. Levin 		},
321*bc6fa711SDmitry V. Levin 	};
322*bc6fa711SDmitry V. Levin 
323*bc6fa711SDmitry V. Levin 	long rc;
324*bc6fa711SDmitry V. Levin 	unsigned int i;
325*bc6fa711SDmitry V. Levin 
326*bc6fa711SDmitry V. Levin 	tracee_pid = fork();
327*bc6fa711SDmitry V. Levin 
328*bc6fa711SDmitry V. Levin 	ASSERT_LE(0, tracee_pid) {
329*bc6fa711SDmitry V. Levin 		TH_LOG("fork: %m");
330*bc6fa711SDmitry V. Levin 	}
331*bc6fa711SDmitry V. Levin 
332*bc6fa711SDmitry V. Levin 	if (tracee_pid == 0) {
333*bc6fa711SDmitry V. Levin 		/* get the pid before PTRACE_TRACEME */
334*bc6fa711SDmitry V. Levin 		tracee_pid = getpid();
335*bc6fa711SDmitry V. Levin 		ASSERT_EQ(0, sys_ptrace(PTRACE_TRACEME, 0, 0, 0)) {
336*bc6fa711SDmitry V. Levin 			TH_LOG("PTRACE_TRACEME: %m");
337*bc6fa711SDmitry V. Levin 		}
338*bc6fa711SDmitry V. Levin 		ASSERT_EQ(0, kill(tracee_pid, SIGSTOP)) {
339*bc6fa711SDmitry V. Levin 			/* cannot happen */
340*bc6fa711SDmitry V. Levin 			TH_LOG("kill SIGSTOP: %m");
341*bc6fa711SDmitry V. Levin 		}
342*bc6fa711SDmitry V. Levin 		for (i = 0; i < ARRAY_SIZE(si); ++i) {
343*bc6fa711SDmitry V. Levin 			rc = syscall(si[i].entry[0].nr,
344*bc6fa711SDmitry V. Levin 				     si[i].entry[0].args[0],
345*bc6fa711SDmitry V. Levin 				     si[i].entry[0].args[1],
346*bc6fa711SDmitry V. Levin 				     si[i].entry[0].args[2],
347*bc6fa711SDmitry V. Levin 				     si[i].entry[0].args[3],
348*bc6fa711SDmitry V. Levin 				     si[i].entry[0].args[4],
349*bc6fa711SDmitry V. Levin 				     si[i].entry[0].args[5]);
350*bc6fa711SDmitry V. Levin 			if (si[i].exit[1].is_error) {
351*bc6fa711SDmitry V. Levin 				if (rc != -1 || errno != -si[i].exit[1].rval)
352*bc6fa711SDmitry V. Levin 					break;
353*bc6fa711SDmitry V. Levin 			} else {
354*bc6fa711SDmitry V. Levin 				if (rc != si[i].exit[1].rval)
355*bc6fa711SDmitry V. Levin 					break;
356*bc6fa711SDmitry V. Levin 			}
357*bc6fa711SDmitry V. Levin 		}
358*bc6fa711SDmitry V. Levin 		/*
359*bc6fa711SDmitry V. Levin 		 * Something went wrong, but in this state tracee
360*bc6fa711SDmitry V. Levin 		 * cannot reliably issue syscalls, so just crash.
361*bc6fa711SDmitry V. Levin 		 */
362*bc6fa711SDmitry V. Levin 		*(volatile unsigned char *) (uintptr_t) i = 42;
363*bc6fa711SDmitry V. Levin 		/* unreachable */
364*bc6fa711SDmitry V. Levin 		_exit(i + 1);
365*bc6fa711SDmitry V. Levin 	}
366*bc6fa711SDmitry V. Levin 
367*bc6fa711SDmitry V. Levin 	for (ptrace_stop = 0; ; ++ptrace_stop) {
368*bc6fa711SDmitry V. Levin 		struct ptrace_syscall_info info = {
369*bc6fa711SDmitry V. Levin 			.op = 0xff	/* invalid PTRACE_SYSCALL_INFO_* op */
370*bc6fa711SDmitry V. Levin 		};
371*bc6fa711SDmitry V. Levin 		const size_t size = sizeof(info);
372*bc6fa711SDmitry V. Levin 		const int expected_entry_size =
373*bc6fa711SDmitry V. Levin 			(void *) &info.entry.args[6] - (void *) &info;
374*bc6fa711SDmitry V. Levin 		const int expected_exit_size =
375*bc6fa711SDmitry V. Levin 			(void *) (&info.exit.is_error + 1) -
376*bc6fa711SDmitry V. Levin 			(void *) &info;
377*bc6fa711SDmitry V. Levin 		int status;
378*bc6fa711SDmitry V. Levin 
379*bc6fa711SDmitry V. Levin 		ASSERT_EQ(tracee_pid, wait(&status)) {
380*bc6fa711SDmitry V. Levin 			/* cannot happen */
381*bc6fa711SDmitry V. Levin 			LOG_KILL_TRACEE("wait: %m");
382*bc6fa711SDmitry V. Levin 		}
383*bc6fa711SDmitry V. Levin 		if (WIFEXITED(status)) {
384*bc6fa711SDmitry V. Levin 			tracee_pid = 0;	/* the tracee is no more */
385*bc6fa711SDmitry V. Levin 			ASSERT_EQ(0, WEXITSTATUS(status)) {
386*bc6fa711SDmitry V. Levin 				LOG_KILL_TRACEE("unexpected exit status %u",
387*bc6fa711SDmitry V. Levin 						WEXITSTATUS(status));
388*bc6fa711SDmitry V. Levin 			}
389*bc6fa711SDmitry V. Levin 			break;
390*bc6fa711SDmitry V. Levin 		}
391*bc6fa711SDmitry V. Levin 		ASSERT_FALSE(WIFSIGNALED(status)) {
392*bc6fa711SDmitry V. Levin 			tracee_pid = 0;	/* the tracee is no more */
393*bc6fa711SDmitry V. Levin 			LOG_KILL_TRACEE("unexpected signal %u",
394*bc6fa711SDmitry V. Levin 					WTERMSIG(status));
395*bc6fa711SDmitry V. Levin 		}
396*bc6fa711SDmitry V. Levin 		ASSERT_TRUE(WIFSTOPPED(status)) {
397*bc6fa711SDmitry V. Levin 			/* cannot happen */
398*bc6fa711SDmitry V. Levin 			LOG_KILL_TRACEE("unexpected wait status %#x", status);
399*bc6fa711SDmitry V. Levin 		}
400*bc6fa711SDmitry V. Levin 
401*bc6fa711SDmitry V. Levin 		ASSERT_LT(ptrace_stop, ARRAY_SIZE(si) * 2) {
402*bc6fa711SDmitry V. Levin 			LOG_KILL_TRACEE("ptrace stop overflow");
403*bc6fa711SDmitry V. Levin 		}
404*bc6fa711SDmitry V. Levin 
405*bc6fa711SDmitry V. Levin 		switch (WSTOPSIG(status)) {
406*bc6fa711SDmitry V. Levin 		case SIGSTOP:
407*bc6fa711SDmitry V. Levin 			ASSERT_EQ(0, ptrace_stop) {
408*bc6fa711SDmitry V. Levin 				LOG_KILL_TRACEE("unexpected signal stop");
409*bc6fa711SDmitry V. Levin 			}
410*bc6fa711SDmitry V. Levin 			ASSERT_EQ(0, sys_ptrace(PTRACE_SETOPTIONS, tracee_pid,
411*bc6fa711SDmitry V. Levin 						0, PTRACE_O_TRACESYSGOOD)) {
412*bc6fa711SDmitry V. Levin 				LOG_KILL_TRACEE("PTRACE_SETOPTIONS: %m");
413*bc6fa711SDmitry V. Levin 			}
414*bc6fa711SDmitry V. Levin 			break;
415*bc6fa711SDmitry V. Levin 
416*bc6fa711SDmitry V. Levin 		case SIGTRAP | 0x80:
417*bc6fa711SDmitry V. Levin 			ASSERT_LT(0, ptrace_stop) {
418*bc6fa711SDmitry V. Levin 				LOG_KILL_TRACEE("unexpected syscall stop");
419*bc6fa711SDmitry V. Levin 			}
420*bc6fa711SDmitry V. Levin 			ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
421*bc6fa711SDmitry V. Levin 						      tracee_pid, size,
422*bc6fa711SDmitry V. Levin 						      (uintptr_t) &info))) {
423*bc6fa711SDmitry V. Levin 				LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #1: %m");
424*bc6fa711SDmitry V. Levin 			}
425*bc6fa711SDmitry V. Levin 			if (ptrace_stop & 1) {
426*bc6fa711SDmitry V. Levin 				/* entering syscall */
427*bc6fa711SDmitry V. Levin 				const struct si_entry *exp_entry =
428*bc6fa711SDmitry V. Levin 					&si[ptrace_stop / 2].entry[0];
429*bc6fa711SDmitry V. Levin 				const struct si_entry *set_entry =
430*bc6fa711SDmitry V. Levin 					&si[ptrace_stop / 2].entry[1];
431*bc6fa711SDmitry V. Levin 
432*bc6fa711SDmitry V. Levin 				/* check ptrace_syscall_info before the changes */
433*bc6fa711SDmitry V. Levin 				ASSERT_EQ(expected_entry_size, rc) {
434*bc6fa711SDmitry V. Levin 					LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #1"
435*bc6fa711SDmitry V. Levin 							": entry stop mismatch");
436*bc6fa711SDmitry V. Levin 				}
437*bc6fa711SDmitry V. Levin 				check_psi_entry(_metadata, &info, exp_entry,
438*bc6fa711SDmitry V. Levin 						"PTRACE_GET_SYSCALL_INFO #1");
439*bc6fa711SDmitry V. Levin 
440*bc6fa711SDmitry V. Levin 				/* apply the changes */
441*bc6fa711SDmitry V. Levin 				info.entry.nr = set_entry->nr;
442*bc6fa711SDmitry V. Levin 				for (i = 0; i < ARRAY_SIZE(set_entry->args); ++i)
443*bc6fa711SDmitry V. Levin 					info.entry.args[i] = set_entry->args[i];
444*bc6fa711SDmitry V. Levin 				ASSERT_EQ(0, sys_ptrace(PTRACE_SET_SYSCALL_INFO,
445*bc6fa711SDmitry V. Levin 							tracee_pid, size,
446*bc6fa711SDmitry V. Levin 							(uintptr_t) &info)) {
447*bc6fa711SDmitry V. Levin 					LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m");
448*bc6fa711SDmitry V. Levin 				}
449*bc6fa711SDmitry V. Levin 
450*bc6fa711SDmitry V. Levin 				/* check ptrace_syscall_info after the changes */
451*bc6fa711SDmitry V. Levin 				memset(&info, 0, sizeof(info));
452*bc6fa711SDmitry V. Levin 				info.op = 0xff;
453*bc6fa711SDmitry V. Levin 				ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
454*bc6fa711SDmitry V. Levin 							      tracee_pid, size,
455*bc6fa711SDmitry V. Levin 							      (uintptr_t) &info))) {
456*bc6fa711SDmitry V. Levin 					LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO: %m");
457*bc6fa711SDmitry V. Levin 				}
458*bc6fa711SDmitry V. Levin 				ASSERT_EQ(expected_entry_size, rc) {
459*bc6fa711SDmitry V. Levin 					LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #2"
460*bc6fa711SDmitry V. Levin 							": entry stop mismatch");
461*bc6fa711SDmitry V. Levin 				}
462*bc6fa711SDmitry V. Levin 				check_psi_entry(_metadata, &info, set_entry,
463*bc6fa711SDmitry V. Levin 						"PTRACE_GET_SYSCALL_INFO #2");
464*bc6fa711SDmitry V. Levin 			} else {
465*bc6fa711SDmitry V. Levin 				/* exiting syscall */
466*bc6fa711SDmitry V. Levin 				const struct si_exit *exp_exit =
467*bc6fa711SDmitry V. Levin 					&si[ptrace_stop / 2 - 1].exit[0];
468*bc6fa711SDmitry V. Levin 				const struct si_exit *set_exit =
469*bc6fa711SDmitry V. Levin 					&si[ptrace_stop / 2 - 1].exit[1];
470*bc6fa711SDmitry V. Levin 
471*bc6fa711SDmitry V. Levin 				/* check ptrace_syscall_info before the changes */
472*bc6fa711SDmitry V. Levin 				ASSERT_EQ(expected_exit_size, rc) {
473*bc6fa711SDmitry V. Levin 					LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #1"
474*bc6fa711SDmitry V. Levin 							": exit stop mismatch");
475*bc6fa711SDmitry V. Levin 				}
476*bc6fa711SDmitry V. Levin 				check_psi_exit(_metadata, &info, exp_exit,
477*bc6fa711SDmitry V. Levin 						"PTRACE_GET_SYSCALL_INFO #1");
478*bc6fa711SDmitry V. Levin 
479*bc6fa711SDmitry V. Levin 				/* apply the changes */
480*bc6fa711SDmitry V. Levin 				info.exit.is_error = set_exit->is_error;
481*bc6fa711SDmitry V. Levin 				info.exit.rval = set_exit->rval;
482*bc6fa711SDmitry V. Levin 				ASSERT_EQ(0, sys_ptrace(PTRACE_SET_SYSCALL_INFO,
483*bc6fa711SDmitry V. Levin 							tracee_pid, size,
484*bc6fa711SDmitry V. Levin 							(uintptr_t) &info)) {
485*bc6fa711SDmitry V. Levin 					LOG_KILL_TRACEE("PTRACE_SET_SYSCALL_INFO: %m");
486*bc6fa711SDmitry V. Levin 				}
487*bc6fa711SDmitry V. Levin 
488*bc6fa711SDmitry V. Levin 				/* check ptrace_syscall_info after the changes */
489*bc6fa711SDmitry V. Levin 				memset(&info, 0, sizeof(info));
490*bc6fa711SDmitry V. Levin 				info.op = 0xff;
491*bc6fa711SDmitry V. Levin 				ASSERT_LT(0, (rc = sys_ptrace(PTRACE_GET_SYSCALL_INFO,
492*bc6fa711SDmitry V. Levin 							      tracee_pid, size,
493*bc6fa711SDmitry V. Levin 							      (uintptr_t) &info))) {
494*bc6fa711SDmitry V. Levin 					LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #2: %m");
495*bc6fa711SDmitry V. Levin 				}
496*bc6fa711SDmitry V. Levin 				ASSERT_EQ(expected_exit_size, rc) {
497*bc6fa711SDmitry V. Levin 					LOG_KILL_TRACEE("PTRACE_GET_SYSCALL_INFO #2"
498*bc6fa711SDmitry V. Levin 							": exit stop mismatch");
499*bc6fa711SDmitry V. Levin 				}
500*bc6fa711SDmitry V. Levin 				check_psi_exit(_metadata, &info, set_exit,
501*bc6fa711SDmitry V. Levin 						"PTRACE_GET_SYSCALL_INFO #2");
502*bc6fa711SDmitry V. Levin 			}
503*bc6fa711SDmitry V. Levin 			break;
504*bc6fa711SDmitry V. Levin 
505*bc6fa711SDmitry V. Levin 		default:
506*bc6fa711SDmitry V. Levin 			LOG_KILL_TRACEE("unexpected stop signal %u",
507*bc6fa711SDmitry V. Levin 					WSTOPSIG(status));
508*bc6fa711SDmitry V. Levin 			abort();
509*bc6fa711SDmitry V. Levin 		}
510*bc6fa711SDmitry V. Levin 
511*bc6fa711SDmitry V. Levin 		ASSERT_EQ(0, sys_ptrace(PTRACE_SYSCALL, tracee_pid, 0, 0)) {
512*bc6fa711SDmitry V. Levin 			LOG_KILL_TRACEE("PTRACE_SYSCALL: %m");
513*bc6fa711SDmitry V. Levin 		}
514*bc6fa711SDmitry V. Levin 	}
515*bc6fa711SDmitry V. Levin 
516*bc6fa711SDmitry V. Levin 	ASSERT_EQ(ptrace_stop, ARRAY_SIZE(si) * 2);
517*bc6fa711SDmitry V. Levin }
518*bc6fa711SDmitry V. Levin 
519*bc6fa711SDmitry V. Levin TEST_HARNESS_MAIN
520