2 * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
3 * Copyright (c) 2018-2019 The strace developers.
6 * SPDX-License-Identifier: LGPL-2.1-or-later
10 #include "kill_save_errno.h"
11 #include "print_fields.h"
13 #include "ptrace_syscall_info.h"
19 #include "xlat/ptrace_syscall_info_op.h"
21 bool ptrace_get_syscall_info_supported;
24 kill_tracee(pid_t pid)
26 return kill_save_errno(pid, SIGKILL);
29 #define FAIL do { ptrace_stop = -1U; goto done; } while (0)
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 static const unsigned int expected_exit_size =
36 offsetofend(struct ptrace_syscall_info, exit.is_error);
37 static const unsigned int expected_seccomp_size =
38 offsetofend(struct ptrace_syscall_info, seccomp.ret_data);
41 * Test that PTRACE_GET_SYSCALL_INFO API is supported by the kernel, and
42 * that the semantics implemented in the kernel matches our expectations.
45 test_ptrace_get_syscall_info(void)
48 * NOMMU provides no forks necessary for PTRACE_GET_SYSCALL_INFO test,
49 * leave the default unchanged.
52 static const unsigned long args[][7] = {
53 /* a sequence of architecture-agnostic syscalls */
82 const unsigned long *exp_args;
86 perror_func_msg_and_die("fork");
89 /* get the pid before PTRACE_TRACEME */
91 if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0) {
92 /* exit with a nonzero exit status */
93 perror_func_msg_and_die("PTRACE_TRACEME");
96 for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
98 args[i][1], args[i][2], args[i][3],
99 args[i][4], args[i][5], args[i][6]);
106 unsigned int is_error;
108 } *exp_param, exit_param[] = {
109 { 1, -ENOENT }, /* chdir */
110 { 0, pid } /* gettid */
113 unsigned int ptrace_stop;
115 for (ptrace_stop = 0; ; ++ptrace_stop) {
116 struct ptrace_syscall_info info = {
117 .op = 0xff /* invalid PTRACE_SYSCALL_INFO_* op */
119 const size_t size = sizeof(info);
121 long rc = waitpid(pid, &status, 0);
125 perror_func_msg_and_die("#%d: unexpected wait result"
126 " %ld", ptrace_stop, rc);
128 if (WIFEXITED(status)) {
129 /* tracee is no more */
131 if (WEXITSTATUS(status) == 0)
133 debug_func_msg("#%d: unexpected exit status %u",
134 ptrace_stop, WEXITSTATUS(status));
137 if (WIFSIGNALED(status)) {
138 /* tracee is no more */
140 debug_func_msg("#%d: unexpected signal %u",
141 ptrace_stop, WTERMSIG(status));
144 if (!WIFSTOPPED(status)) {
147 error_func_msg_and_die("#%d: unexpected wait status"
148 " %#x", ptrace_stop, status);
151 switch (WSTOPSIG(status)) {
154 debug_func_msg("#%d: unexpected signal stop",
158 if (ptrace(PTRACE_SETOPTIONS, pid, 0L,
159 PTRACE_O_TRACESYSGOOD) < 0) {
162 perror_func_msg_and_die("PTRACE_SETOPTIONS");
164 rc = ptrace(PTRACE_GET_SYSCALL_INFO, pid,
165 (void *) size, &info);
167 debug_perror_msg("PTRACE_GET_SYSCALL_INFO");
170 if (rc < (long) expected_none_size
171 || info.op != PTRACE_SYSCALL_INFO_NONE
173 || !info.instruction_pointer
174 || !info.stack_pointer) {
175 debug_func_msg("signal stop mismatch");
181 rc = ptrace(PTRACE_GET_SYSCALL_INFO, pid,
182 (void *) size, &info);
184 debug_perror_msg("#%d: PTRACE_GET_SYSCALL_INFO",
188 switch (ptrace_stop) {
189 case 1: /* entering chdir */
190 case 3: /* entering gettid */
191 case 5: /* entering exit_group */
192 exp_args = args[ptrace_stop / 2];
193 if (rc < (long) expected_entry_size
194 || info.op != PTRACE_SYSCALL_INFO_ENTRY
196 || !info.instruction_pointer
197 || !info.stack_pointer
198 || (info.entry.nr != exp_args[0])
199 || (info.entry.args[0] != exp_args[1])
200 || (info.entry.args[1] != exp_args[2])
201 || (info.entry.args[2] != exp_args[3])
202 || (info.entry.args[3] != exp_args[4])
203 || (info.entry.args[4] != exp_args[5])
204 || (info.entry.args[5] != exp_args[6])) {
205 debug_func_msg("#%d: entry stop"
211 case 2: /* exiting chdir */
212 case 4: /* exiting gettid */
213 exp_param = &exit_param[ptrace_stop / 2 - 1];
214 if (rc < (long) expected_exit_size
215 || info.op != PTRACE_SYSCALL_INFO_EXIT
217 || !info.instruction_pointer
218 || !info.stack_pointer
219 || info.exit.is_error != exp_param->is_error
220 || info.exit.rval != exp_param->rval) {
221 debug_func_msg("#%d: exit stop"
228 debug_func_msg("#%d: unexpected syscall stop",
235 debug_func_msg("#%d: unexpected stop signal %#x",
236 ptrace_stop, WSTOPSIG(status));
240 if (ptrace(PTRACE_SYSCALL, pid, 0L, 0L) < 0) {
243 perror_func_msg_and_die("PTRACE_SYSCALL");
250 waitpid(pid, NULL, 0);
254 ptrace_get_syscall_info_supported =
255 ptrace_stop == ARRAY_SIZE(args) * 2;
257 if (ptrace_get_syscall_info_supported)
258 debug_msg("PTRACE_GET_SYSCALL_INFO works");
260 debug_msg("PTRACE_GET_SYSCALL_INFO does not work");
261 #endif /* HAVE_FORK */
263 return ptrace_get_syscall_info_supported;
267 print_ptrace_syscall_info(struct tcb *tcp, kernel_ulong_t addr,
268 kernel_ulong_t user_len)
270 struct ptrace_syscall_info info;
271 kernel_ulong_t kernel_len = tcp->u_rval;
272 kernel_ulong_t ret_len = MIN(user_len, kernel_len);
273 kernel_ulong_t fetch_size = MIN(ret_len, expected_seccomp_size);
275 if (!fetch_size || !tfetch_mem(tcp, addr, fetch_size, &info)) {
280 PRINT_FIELD_XVAL("{", info, op, ptrace_syscall_info_op,
281 "PTRACE_SYSCALL_INFO_???");
282 if (fetch_size < offsetofend(struct ptrace_syscall_info, arch))
284 PRINT_FIELD_XVAL(", ", info, arch, audit_arch, "AUDIT_ARCH_???");
286 if (fetch_size < offsetofend(struct ptrace_syscall_info,
287 instruction_pointer))
289 PRINT_FIELD_ADDR64(", ", info, instruction_pointer);
291 if (fetch_size < offsetofend(struct ptrace_syscall_info, stack_pointer))
293 PRINT_FIELD_ADDR64(", ", info, stack_pointer);
295 if (fetch_size < offsetofend(struct ptrace_syscall_info, entry.nr))
299 case PTRACE_SYSCALL_INFO_ENTRY:
300 case PTRACE_SYSCALL_INFO_SECCOMP:
301 PRINT_FIELD_U((info.op == PTRACE_SYSCALL_INFO_ENTRY
302 ? ", entry={" : ", seccomp={"),
304 for (unsigned int i = 0;
305 i < ARRAY_SIZE(info.entry.args); ++i) {
306 const unsigned int i_size =
307 offsetofend(struct ptrace_syscall_info,
309 if (fetch_size < i_size) {
314 tprintf(", %s%#" PRIx64,
316 (uint64_t) info.entry.args[i]);
319 if (info.op == PTRACE_SYSCALL_INFO_SECCOMP
320 && fetch_size >= expected_seccomp_size)
321 PRINT_FIELD_U(", ", info.seccomp, ret_data);
325 case PTRACE_SYSCALL_INFO_EXIT:
327 if (fetch_size >= expected_exit_size
328 && info.exit.is_error) {
329 PRINT_FIELD_ERR_D("", info.exit, rval);
331 PRINT_FIELD_D("", info.exit, rval);
333 if (fetch_size >= expected_exit_size)
334 PRINT_FIELD_U(", ", info.exit, is_error);