2 * Check decoding of ptrace PTRACE_GET_SYSCALL_INFO request.
4 * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
7 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include <asm/unistd.h>
23 #include <linux/audit.h>
26 #include "xlat/audit_arch.h"
28 static const char *errstr;
31 do_ptrace(unsigned long request, unsigned long pid,
32 unsigned long addr, unsigned long data)
34 long rc = syscall(__NR_ptrace, request, pid, addr, data);
35 errstr = sprintrc(rc);
46 int saved_errno = errno;
51 #define FAIL(fmt_, ...) \
54 error_msg_and_fail("%s:%d: " fmt_, \
55 __FILE__, __LINE__, ##__VA_ARGS__); \
58 #define PFAIL(fmt_, ...) \
61 perror_msg_and_fail("%s:%d: " fmt_, \
62 __FILE__, __LINE__, ##__VA_ARGS__); \
65 static const unsigned long args[][7] = {
66 /* a sequence of architecture-agnostic syscalls */
96 static const unsigned int expected_none_size =
97 offsetof(struct ptrace_syscall_info, entry);
98 static const unsigned int expected_entry_size =
99 offsetofend(struct ptrace_syscall_info, entry.args);
100 static const unsigned int expected_exit_size =
101 offsetofend(struct ptrace_syscall_info, exit.is_error);
103 static unsigned long end_of_page;
104 static unsigned int ptrace_stop;
109 do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, 1, 0);
110 printf("ptrace(PTRACE_GET_SYSCALL_INFO, %d, 1, NULL) = %s\n",
113 do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, 1, end_of_page);
114 printf("ptrace(PTRACE_GET_SYSCALL_INFO, %d, 1, %#lx) = %s\n",
115 pid, end_of_page, errstr);
117 for (unsigned int size = 0;
118 size <= sizeof(struct ptrace_syscall_info); ++size) {
119 unsigned long buf = end_of_page - size;
120 memset((void *) buf, -1, size);
122 long rc = do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, buf);
124 printf("ptrace(PTRACE_GET_SYSCALL_INFO, %d, %u, %#lx)"
126 pid, (unsigned int) size, buf, errstr);
129 if (rc < (long) expected_none_size)
130 FAIL("signal stop mismatch");
132 printf("ptrace(PTRACE_GET_SYSCALL_INFO, %d, %u, ",
135 printf("%#lx) = %s\n", buf, errstr);
139 /* copy to a local structure to avoid unaligned access */
140 struct ptrace_syscall_info info;
141 memcpy(&info, (void *) buf, MIN(size, expected_none_size));
143 if (info.op != PTRACE_SYSCALL_INFO_NONE)
144 FAIL("signal stop mismatch");
145 printf("{op=PTRACE_SYSCALL_INFO_NONE");
147 if (size < offsetofend(struct ptrace_syscall_info, arch))
150 FAIL("signal stop mismatch");
152 printxval(audit_arch, info.arch, "AUDIT_ARCH_???");
154 if (size < offsetofend(struct ptrace_syscall_info,
155 instruction_pointer))
157 if (!info.instruction_pointer)
158 FAIL("signal stop mismatch");
159 printf(", instruction_pointer=%#llx",
160 (unsigned long long) info.instruction_pointer);
162 if (size < offsetofend(struct ptrace_syscall_info,
165 if (!info.stack_pointer)
166 FAIL("signal stop mismatch");
167 printf(", stack_pointer=%#llx",
168 (unsigned long long) info.stack_pointer);
171 printf("}) = %s\n", errstr);
180 for (unsigned int size = 0;
181 size <= sizeof(struct ptrace_syscall_info); ++size) {
182 unsigned long buf = end_of_page - size;
183 memset((void *) buf, -1, size);
185 long rc = do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, buf);
187 PFAIL("PTRACE_GET_SYSCALL_INFO");
189 if (rc < (long) expected_entry_size)
190 FAIL("#%d: entry stop mismatch", ptrace_stop);
192 printf("ptrace(PTRACE_GET_SYSCALL_INFO, %d, %u, ",
195 printf("%#lx) = %s\n", buf, errstr);
199 /* copy to a local structure to avoid unaligned access */
200 struct ptrace_syscall_info info;
201 memcpy(&info, (void *) buf, MIN(size, expected_entry_size));
203 if (info.op != PTRACE_SYSCALL_INFO_ENTRY)
204 FAIL("#%d: entry stop mismatch", ptrace_stop);
205 printf("{op=PTRACE_SYSCALL_INFO_ENTRY");
207 if (size < offsetofend(struct ptrace_syscall_info, arch))
208 goto printed_entry_common;
210 FAIL("#%d: entry stop mismatch", ptrace_stop);
212 printxval(audit_arch, info.arch, "AUDIT_ARCH_???");
214 if (size < offsetofend(struct ptrace_syscall_info,
215 instruction_pointer))
216 goto printed_entry_common;
217 if (!info.instruction_pointer)
218 FAIL("#%d: entry stop mismatch", ptrace_stop);
219 printf(", instruction_pointer=%#llx",
220 (unsigned long long) info.instruction_pointer);
222 if (size < offsetofend(struct ptrace_syscall_info,
224 goto printed_entry_common;
225 if (!info.stack_pointer)
226 FAIL("#%d: entry stop mismatch", ptrace_stop);
227 printf(", stack_pointer=%#llx",
228 (unsigned long long) info.stack_pointer);
230 if (size < offsetofend(struct ptrace_syscall_info, entry.nr))
231 goto printed_entry_common;
232 const unsigned long *exp_args = args[ptrace_stop / 2];
233 if (info.entry.nr != exp_args[0])
234 FAIL("#%d: entry stop mismatch", ptrace_stop);
235 printf(", entry={nr=%llu", (unsigned long long) info.entry.nr);
237 for (unsigned int i = 0; i < ARRAY_SIZE(info.entry.args); ++i) {
238 const unsigned int i_size =
239 offsetofend(struct ptrace_syscall_info,
244 goto printed_entry_nr;
246 if (info.entry.args[i] != exp_args[i + 1])
247 FAIL("#%d: entry stop mismatch", ptrace_stop);
248 printf("%s%#llx", (i ? ", " : ", arg=["),
249 (unsigned long long) info.entry.args[i]);
256 printed_entry_common:
257 printf("}) = %s\n", errstr);
264 for (unsigned int size = 0;
265 size <= sizeof(struct ptrace_syscall_info); ++size) {
266 unsigned long buf = end_of_page - size;
267 memset((void *) buf, -1, size);
269 long rc = do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, buf);
271 PFAIL("PTRACE_GET_SYSCALL_INFO");
273 if (rc < (long) expected_exit_size)
274 FAIL("#%d: exit stop mismatch", ptrace_stop);
276 printf("ptrace(PTRACE_GET_SYSCALL_INFO, %d, %u, ",
279 printf("%#lx) = %s\n", buf, errstr);
283 /* copy to a local structure to avoid unaligned access */
284 struct ptrace_syscall_info info;
285 memcpy(&info, (void *) buf, MIN(size, expected_exit_size));
287 if (info.op != PTRACE_SYSCALL_INFO_EXIT)
288 FAIL("#%d: exit stop mismatch", ptrace_stop);
289 printf("{op=PTRACE_SYSCALL_INFO_EXIT");
291 if (size < offsetofend(struct ptrace_syscall_info, arch))
292 goto printed_exit_common;
294 FAIL("#%d: exit stop mismatch", ptrace_stop);
296 printxval(audit_arch, info.arch, "AUDIT_ARCH_???");
298 if (size < offsetofend(struct ptrace_syscall_info,
299 instruction_pointer))
300 goto printed_exit_common;
301 if (!info.instruction_pointer)
302 FAIL("#%d: exit stop mismatch", ptrace_stop);
303 printf(", instruction_pointer=%#llx",
304 (unsigned long long) info.instruction_pointer);
306 if (size < offsetofend(struct ptrace_syscall_info,
308 goto printed_exit_common;
309 if (!info.stack_pointer)
310 FAIL("#%d: exit stop mismatch", ptrace_stop);
311 printf(", stack_pointer=%#llx",
312 (unsigned long long) info.stack_pointer);
315 unsigned int is_error;
319 { 1, -ENOENT, "-ENOENT" }, /* chdir */
320 { 0, pid, NULL } /* gettid */
321 }, *exp_param = &exit_param[ptrace_stop / 2 - 1];
323 if (size < offsetofend(struct ptrace_syscall_info, exit.rval))
324 goto printed_exit_common;
325 if (info.exit.rval != exp_param->rval)
326 FAIL("#%d: exit stop mismatch", ptrace_stop);
327 if (size >= expected_exit_size && info.exit.is_error) {
328 printf(", exit={rval=%s", exp_param->str);
330 printf(", exit={rval=%lld", (long long) info.exit.rval);
333 if (size >= expected_exit_size) {
334 if (info.exit.is_error != exp_param->is_error)
335 FAIL("#%d: exit stop mismatch", ptrace_stop);
336 printf(", is_error=%u",
337 (unsigned int) info.exit.is_error);
343 printf("}) = %s\n", errstr);
350 end_of_page = (unsigned long) tail_alloc(1) + 1;
353 do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, 0, 0);
354 printf("ptrace(PTRACE_GET_SYSCALL_INFO, %d, 0, NULL) = %s\n",
362 /* get the pid before PTRACE_TRACEME */
364 if (do_ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
365 /* exit with a nonzero exit status */
366 PFAIL("PTRACE_TRACEME");
369 for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
371 args[i][1], args[i][2], args[i][3],
372 args[i][4], args[i][5], args[i][6]);
378 for (ptrace_stop = 0; ; ++ptrace_stop) {
380 long rc = waitpid(pid, &status, 0);
383 PFAIL("#%d: unexpected wait result %ld",
386 if (WIFEXITED(status)) {
387 /* tracee is no more */
389 if (WEXITSTATUS(status) == 0)
391 FAIL("#%d: unexpected exit status %u",
392 ptrace_stop, WEXITSTATUS(status));
394 if (WIFSIGNALED(status)) {
395 /* tracee is no more */
397 FAIL("#%d: unexpected signal %u",
398 ptrace_stop, WTERMSIG(status));
400 if (!WIFSTOPPED(status)) {
402 FAIL("#%d: unexpected wait status %#x",
403 ptrace_stop, status);
406 switch (WSTOPSIG(status)) {
409 FAIL("#%d: unexpected signal stop",
411 if (do_ptrace(PTRACE_SETOPTIONS, pid, 0,
412 PTRACE_O_TRACESYSGOOD) < 0) {
414 PFAIL("PTRACE_SETOPTIONS");
416 printf("ptrace(PTRACE_SETOPTIONS, %d, NULL"
417 ", PTRACE_O_TRACESYSGOOD) = 0\n", pid);
424 switch (ptrace_stop) {
425 case 1: /* entering chdir */
426 case 3: /* entering gettid */
427 case 5: /* entering exit_group */
430 case 2: /* exiting chdir */
431 case 4: /* exiting gettid */
435 FAIL("#%d: unexpected syscall stop",
441 FAIL("#%d: unexpected stop signal %#x",
442 ptrace_stop, WSTOPSIG(status));
445 if (do_ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) {
447 PFAIL("PTRACE_SYSCALL");
449 printf("ptrace(PTRACE_SYSCALL, %d, NULL, 0) = 0\n", pid);
455 waitpid(pid, NULL, 0);
458 puts("+++ exited with 0 +++");