1 /*
2 * Copyright (c) 2018 Dmitry V. Levin <ldv@strace.io>
3 * Copyright (c) 2018-2021 The strace developers.
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8
9 #include "defs.h"
10 #include "kill_save_errno.h"
11 #include "ptrace.h"
12 #include "ptrace_syscall_info.h"
13 #include "scno.h"
14
15 #include <signal.h>
16 #include <sys/wait.h>
17
18 #include "xlat/ptrace_syscall_info_op.h"
19
20 bool ptrace_get_syscall_info_supported;
21
22 #define FAIL do { ptrace_stop = -1U; goto done; } while (0)
23
24 #ifdef HAVE_FORK
25 static int
26 kill_tracee(pid_t pid)
27 {
28 return kill_save_errno(pid, SIGKILL);
29 }
30
31 static const unsigned int expected_none_size =
32 offsetof(struct_ptrace_syscall_info, entry);
33 static const unsigned int expected_entry_size =
34 offsetofend(struct_ptrace_syscall_info, entry.args);
35 #endif /* HAVE_FORK */
36 static const unsigned int expected_exit_size =
37 offsetofend(struct_ptrace_syscall_info, exit.is_error);
38 static const unsigned int expected_seccomp_size =
39 offsetofend(struct_ptrace_syscall_info, seccomp.ret_data);
40
41 /*
42 * Test that PTRACE_GET_SYSCALL_INFO API is supported by the kernel, and
43 * that the semantics implemented in the kernel matches our expectations.
44 */
45 bool
46 test_ptrace_get_syscall_info(void)
47 {
48 /*
49 * NOMMU provides no forks necessary for PTRACE_GET_SYSCALL_INFO test,
50 * leave the default unchanged.
51 */
52 #ifdef HAVE_FORK
53 static const unsigned long args[][7] = {
54 /* a sequence of architecture-agnostic syscalls */
55 {
56 __NR_chdir,
57 (unsigned long) "",
58 0xbad1fed1,
59 0xbad2fed2,
60 0xbad3fed3,
61 0xbad4fed4,
62 0xbad5fed5
63 },
64 {
65 __NR_gettid,
66 0xcaf0bea0,
67 0xcaf1bea1,
68 0xcaf2bea2,
69 0xcaf3bea3,
70 0xcaf4bea4,
71 0xcaf5bea5
72 },
73 {
74 __NR_exit_group,
75 0,
76 0xfac1c0d1,
77 0xfac2c0d2,
78 0xfac3c0d3,
79 0xfac4c0d4,
80 0xfac5c0d5
81 }
82 };
83 const unsigned long *exp_args;
84
85 # if SIZEOF_KERNEL_LONG_T > SIZEOF_LONG
86 # define CAST (unsigned long)
87 # else
88 # define CAST
89 # endif
90
91 int pid = fork();
92 if (pid < 0)
93 perror_func_msg_and_die("fork");
94
95 if (pid == 0) {
96 /* get the pid before PTRACE_TRACEME */
97 pid = getpid();
98 if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0) {
99 /* exit with a nonzero exit status */
100 perror_func_msg_and_die("PTRACE_TRACEME");
101 }
102 kill(pid, SIGSTOP);
103 for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
104 syscall(args[i][0],
105 args[i][1], args[i][2], args[i][3],
106 args[i][4], args[i][5], args[i][6]);
107 }
108 /* unreachable */
109 _exit(1);
110 }
111
112 const struct {
113 unsigned int is_error;
114 int rval;
115 } *exp_param, exit_param[] = {
116 { 1, -ENOENT }, /* chdir */
117 { 0, pid } /* gettid */
118 };
119
120 unsigned int ptrace_stop;
121
122 for (ptrace_stop = 0; ; ++ptrace_stop) {
123 struct_ptrace_syscall_info info = {
124 .op = 0xff /* invalid PTRACE_SYSCALL_INFO_* op */
125 };
126 const size_t size = sizeof(info);
127 int status;
128 long rc = waitpid(pid, &status, 0);
129 if (rc != pid) {
130 /* cannot happen */
131 kill_tracee(pid);
132 perror_func_msg_and_die("#%d: unexpected wait result"
133 " %ld", ptrace_stop, rc);
134 }
135 if (WIFEXITED(status)) {
136 /* tracee is no more */
137 pid = 0;
138 if (WEXITSTATUS(status) == 0)
139 break;
140 debug_func_msg("#%d: unexpected exit status %u",
141 ptrace_stop, WEXITSTATUS(status));
142 FAIL;
143 }
144 if (WIFSIGNALED(status)) {
145 /* tracee is no more */
146 pid = 0;
147 debug_func_msg("#%d: unexpected signal %u",
148 ptrace_stop, WTERMSIG(status));
149 FAIL;
150 }
151 if (!WIFSTOPPED(status)) {
152 /* cannot happen */
153 kill_tracee(pid);
154 error_func_msg_and_die("#%d: unexpected wait status"
155 " %#x", ptrace_stop, status);
156 }
157
158 switch (WSTOPSIG(status)) {
159 case SIGSTOP:
160 if (ptrace_stop) {
161 debug_func_msg("#%d: unexpected signal stop",
162 ptrace_stop);
163 FAIL;
164 }
165 if (ptrace(PTRACE_SETOPTIONS, pid, 0L,
166 PTRACE_O_TRACESYSGOOD) < 0) {
167 /* cannot happen */
168 kill_tracee(pid);
169 perror_func_msg_and_die("PTRACE_SETOPTIONS");
170 }
171 rc = ptrace(PTRACE_GET_SYSCALL_INFO, pid,
172 (void *) size, &info);
173 if (rc < 0) {
174 debug_perror_msg("PTRACE_GET_SYSCALL_INFO");
175 FAIL;
176 }
177 if (rc < (long) expected_none_size
178 || info.op != PTRACE_SYSCALL_INFO_NONE
179 || !info.arch
180 || !info.instruction_pointer
181 || !info.stack_pointer) {
182 debug_func_msg("signal stop mismatch");
183 FAIL;
184 }
185 break;
186
187 case SIGTRAP | 0x80:
188 rc = ptrace(PTRACE_GET_SYSCALL_INFO, pid,
189 (void *) size, &info);
190 if (rc < 0) {
191 debug_perror_msg("#%d: PTRACE_GET_SYSCALL_INFO",
192 ptrace_stop);
193 FAIL;
194 }
195 switch (ptrace_stop) {
196 case 1: /* entering chdir */
197 case 3: /* entering gettid */
198 case 5: /* entering exit_group */
199 exp_args = args[ptrace_stop / 2];
200 if (rc < (long) expected_entry_size
201 || info.op != PTRACE_SYSCALL_INFO_ENTRY
202 || !info.arch
203 || !info.instruction_pointer
204 || !info.stack_pointer
205 || (info.entry.nr != exp_args[0])
206 || (CAST info.entry.args[0] != exp_args[1])
207 || (CAST info.entry.args[1] != exp_args[2])
208 || (CAST info.entry.args[2] != exp_args[3])
209 || (CAST info.entry.args[3] != exp_args[4])
210 || (CAST info.entry.args[4] != exp_args[5])
211 || (CAST info.entry.args[5] != exp_args[6])) {
212 debug_func_msg("#%d: entry stop"
213 " mismatch",
214 ptrace_stop);
215 FAIL;
216 }
217 break;
218 case 2: /* exiting chdir */
219 case 4: /* exiting gettid */
220 exp_param = &exit_param[ptrace_stop / 2 - 1];
221 if (rc < (long) expected_exit_size
222 || info.op != PTRACE_SYSCALL_INFO_EXIT
223 || !info.arch
224 || !info.instruction_pointer
225 || !info.stack_pointer
226 || info.exit.is_error != exp_param->is_error
227 || info.exit.rval != exp_param->rval) {
228 debug_func_msg("#%d: exit stop"
229 " mismatch",
230 ptrace_stop);
231 FAIL;
232 }
233 break;
234 default:
235 debug_func_msg("#%d: unexpected syscall stop",
236 ptrace_stop);
237 FAIL;
238 }
239 break;
240
241 default:
242 debug_func_msg("#%d: unexpected stop signal %#x",
243 ptrace_stop, WSTOPSIG(status));
244 FAIL;
245 }
246
247 if (ptrace(PTRACE_SYSCALL, pid, 0L, 0L) < 0) {
248 /* cannot happen */
249 kill_tracee(pid);
250 perror_func_msg_and_die("PTRACE_SYSCALL");
251 }
252 }
253
254 done:
255 if (pid) {
256 kill_tracee(pid);
257 waitpid(pid, NULL, 0);
258 ptrace_stop = -1U;
259 }
260
261 ptrace_get_syscall_info_supported =
262 ptrace_stop == ARRAY_SIZE(args) * 2;
263
264 if (ptrace_get_syscall_info_supported)
265 debug_msg("PTRACE_GET_SYSCALL_INFO works");
266 else
267 debug_msg("PTRACE_GET_SYSCALL_INFO does not work");
268 #endif /* HAVE_FORK */
269
270 return ptrace_get_syscall_info_supported;
271 }
272
273 static void
274 print_psi_entry(const typeof_field(struct_ptrace_syscall_info, entry) *const p,
275 const kernel_ulong_t fetch_size, struct tcb *const tcp,
276 unsigned int arch)
277 {
278 tprint_struct_begin();
279 PRINT_FIELD_SYSCALL_NAME(*p, nr, arch);
280 const kernel_ulong_t nargs =
281 (fetch_size - offsetof(struct_ptrace_syscall_info, entry.args))
282 / sizeof(p->args[0]);
283 if (nargs) {
284 tprint_struct_next();
285 PRINT_FIELD_ARRAY_UPTO(*p, args, nargs, tcp,
286 print_xint_array_member);
287 }
288 tprint_struct_end();
289 }
290
291 static void
292 print_psi_seccomp(const typeof_field(struct_ptrace_syscall_info, seccomp) *const p,
293 const kernel_ulong_t fetch_size, struct tcb *const tcp,
294 unsigned int arch)
295 {
296 tprint_struct_begin();
297 PRINT_FIELD_SYSCALL_NAME(*p, nr, arch);
298 const kernel_ulong_t nargs =
299 (fetch_size - offsetof(struct_ptrace_syscall_info, seccomp.args))
300 / sizeof(p->args[0]);
301 if (nargs) {
302 tprint_struct_next();
303 PRINT_FIELD_ARRAY_UPTO(*p, args, nargs, tcp,
304 print_xint_array_member);
305 }
306 if (fetch_size >= expected_seccomp_size) {
307 tprint_struct_next();
308 PRINT_FIELD_U(*p, ret_data);
309 }
310 tprint_struct_end();
311 }
312
313 static void
314 print_psi_exit(const typeof_field(struct_ptrace_syscall_info, exit) *const p,
315 const kernel_ulong_t fetch_size, struct tcb *const tcp)
316 {
317 tprint_struct_begin();
318 if (fetch_size >= expected_exit_size && p->is_error) {
319 PRINT_FIELD_ERR_D(*p, rval);
320 } else {
321 PRINT_FIELD_D(*p, rval);
322 }
323 if (fetch_size >= expected_exit_size) {
324 tprint_struct_next();
325 PRINT_FIELD_U(*p, is_error);
326 }
327 tprint_struct_end();
328 }
329
330 void
331 print_ptrace_syscall_info(struct tcb *tcp, kernel_ulong_t addr,
332 kernel_ulong_t user_len)
333 {
334 struct_ptrace_syscall_info info;
335 kernel_ulong_t kernel_len = tcp->u_rval;
336 kernel_ulong_t ret_len = MIN(user_len, kernel_len);
337 kernel_ulong_t fetch_size = MIN(ret_len, expected_seccomp_size);
338
339 if (!fetch_size || !tfetch_mem(tcp, addr, fetch_size, &info)) {
340 printaddr(addr);
341 return;
342 }
343
344 tprint_struct_begin();
345 PRINT_FIELD_XVAL(info, op, ptrace_syscall_info_op,
346 "PTRACE_SYSCALL_INFO_???");
347 if (fetch_size < offsetofend(struct_ptrace_syscall_info, arch))
348 goto printed;
349 tprint_struct_next();
350 PRINT_FIELD_XVAL(info, arch, audit_arch, "AUDIT_ARCH_???");
351
352 if (fetch_size < offsetofend(struct_ptrace_syscall_info,
353 instruction_pointer))
354 goto printed;
355 tprint_struct_next();
356 PRINT_FIELD_ADDR64(info, instruction_pointer);
357
358 if (fetch_size < offsetofend(struct_ptrace_syscall_info, stack_pointer))
359 goto printed;
360 tprint_struct_next();
361 PRINT_FIELD_ADDR64(info, stack_pointer);
362
363 if (fetch_size < offsetofend(struct_ptrace_syscall_info, entry.nr))
364 goto printed;
365
366 switch(info.op) {
367 case PTRACE_SYSCALL_INFO_ENTRY:
368 tprint_struct_next();
369 PRINT_FIELD_OBJ_PTR(info, entry,
370 print_psi_entry, fetch_size, tcp,
371 info.arch);
372 break;
373 case PTRACE_SYSCALL_INFO_SECCOMP:
374 tprint_struct_next();
375 PRINT_FIELD_OBJ_PTR(info, seccomp,
376 print_psi_seccomp, fetch_size, tcp,
377 info.arch);
378 break;
379 case PTRACE_SYSCALL_INFO_EXIT:
380 tprint_struct_next();
381 PRINT_FIELD_OBJ_PTR(info, exit,
382 print_psi_exit, fetch_size, tcp);
383 break;
384 }
385
386 printed:
387 tprint_struct_end();
388 }