1 /*
2 * Check decoding of ptrace PTRACE_GET_SYSCALL_INFO request.
3 *
4 * Copyright (c) 2018 Dmitry V. Levin <ldv@strace.io>
5 * Copyright (c) 2018-2023 The strace developers.
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "tests.h"
12
13 #include "ptrace.h"
14 #include "scno.h"
15
16 #include <errno.h>
17 #include <stddef.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <signal.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 #include <linux/audit.h>
24
25 #include "xlat.h"
26 #define XLAT_MACROS_ONLY
27 /* For xlat/audit_arch.h */
28 # include "xlat/elf_em.h"
29 #undef XLAT_MACROS_ONLY
30 #include "xlat/audit_arch.h"
31
32 static const char *errstr;
33
34 static long
35 do_ptrace(unsigned long request, unsigned long pid,
36 unsigned long addr, unsigned long data)
37 {
38 long rc = syscall(__NR_ptrace, request, pid, addr, data);
39 errstr = sprintrc(rc);
40 return rc;
41 }
42
43 static pid_t pid;
44
45 static void
46 kill_tracee(void)
47 {
48 if (!pid)
49 return;
50 int saved_errno = errno;
51 kill(pid, SIGKILL);
52 errno = saved_errno;
53 }
54
55 #define FAIL(fmt_, ...) \
56 do { \
57 kill_tracee(); \
58 error_msg_and_fail("%s:%d: " fmt_, \
59 __FILE__, __LINE__, ##__VA_ARGS__); \
60 } while (0)
61
62 #define PFAIL(fmt_, ...) \
63 do { \
64 kill_tracee(); \
65 perror_msg_and_fail("%s:%d: " fmt_, \
66 __FILE__, __LINE__, ##__VA_ARGS__); \
67 } while (0)
68
69 static const unsigned long args[][7] = {
70 /* a sequence of architecture-agnostic syscalls */
71 {
72 __NR_chdir,
73 (unsigned long) "",
74 0xbad1fed1,
75 0xbad2fed2,
76 0xbad3fed3,
77 0xbad4fed4,
78 0xbad5fed5
79 },
80 {
81 __NR_gettid,
82 0xcaf0bea0,
83 0xcaf1bea1,
84 0xcaf2bea2,
85 0xcaf3bea3,
86 0xcaf4bea4,
87 0xcaf5bea5
88 },
89 {
90 __NR_exit_group,
91 0,
92 0xfac1c0d1,
93 0xfac2c0d2,
94 0xfac3c0d3,
95 0xfac4c0d4,
96 0xfac5c0d5
97 }
98 };
99
100 #if !XLAT_RAW
101 static const char *sc_names[] = {
102 "chdir",
103 "gettid",
104 "exit_group"
105 };
106 #endif
107
108 static const unsigned int expected_none_size =
109 offsetof(struct_ptrace_syscall_info, entry);
110 static const unsigned int expected_entry_size =
111 offsetofend(struct_ptrace_syscall_info, entry.args);
112 static const unsigned int expected_exit_size =
113 offsetofend(struct_ptrace_syscall_info, exit.is_error);
114
115 static unsigned long end_of_page;
116 static unsigned int ptrace_stop;
117
118 static bool
119 test_none(void)
120 {
121 do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, 1, 0);
122 printf("ptrace(" XLAT_FMT ", %d, 1, NULL) = %s\n",
123 XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, errstr);
124
125 do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, 1, end_of_page);
126 printf("ptrace(" XLAT_FMT ", %d, 1, %#lx) = %s\n",
127 XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, end_of_page, errstr);
128
129 for (unsigned int size = 0;
130 size <= sizeof(struct_ptrace_syscall_info); ++size) {
131 unsigned long buf = end_of_page - size;
132 memset((void *) buf, -1, size);
133
134 long rc = do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, buf);
135 printf("ptrace(" XLAT_FMT ", %d, %u, ",
136 XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, size);
137 if (rc < (long) expected_none_size || size == 0)
138 printf("%#lx) = %s\n", buf, errstr);
139
140 if (rc < 0)
141 return false;
142 if (rc < (long) expected_none_size)
143 FAIL("signal stop mismatch");
144 if (size == 0)
145 continue;
146
147 /* copy to a local structure to avoid unaligned access */
148 struct_ptrace_syscall_info info;
149 memcpy(&info, (void *) buf, MIN(size, expected_none_size));
150
151 printf("{op=" XLAT_FMT, XLAT_ARGS(PTRACE_SYSCALL_INFO_NONE));
152 if (info.op != PTRACE_SYSCALL_INFO_NONE)
153 FAIL("signal stop mismatch");
154
155 if (size < offsetofend(struct_ptrace_syscall_info, arch))
156 goto printed_none;
157 printf(", arch=");
158 printxval(audit_arch, info.arch, "AUDIT_ARCH_???");
159 if (!info.arch)
160 FAIL("signal stop mismatch");
161
162 if (size < offsetofend(struct_ptrace_syscall_info,
163 instruction_pointer))
164 goto printed_none;
165 printf(", instruction_pointer=%#llx",
166 (unsigned long long) info.instruction_pointer);
167 if (!info.instruction_pointer)
168 FAIL("signal stop mismatch");
169
170 if (size < offsetofend(struct_ptrace_syscall_info,
171 stack_pointer))
172 goto printed_none;
173 printf(", stack_pointer=%#llx",
174 (unsigned long long) info.stack_pointer);
175 if (!info.stack_pointer)
176 FAIL("signal stop mismatch");
177
178 printed_none:
179 printf("}) = %s\n", errstr);
180 }
181
182 return true;
183 }
184
185 static void
186 test_entry(void)
187 {
188 for (unsigned int size = 0;
189 size <= sizeof(struct_ptrace_syscall_info); ++size) {
190 unsigned long buf = end_of_page - size;
191 memset((void *) buf, -1, size);
192
193 long rc = do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, buf);
194 printf("ptrace(" XLAT_FMT ", %d, %u, ",
195 XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, size);
196 if (rc < (long) expected_entry_size || size == 0)
197 printf("%#lx) = %s\n", buf, errstr);
198
199 if (rc < 0)
200 FAIL("PTRACE_GET_SYSCALL_INFO");
201 if (rc < (long) expected_entry_size)
202 FAIL("#%d: entry stop mismatch", ptrace_stop);
203 if (size == 0)
204 continue;
205
206 /* copy to a local structure to avoid unaligned access */
207 struct_ptrace_syscall_info info;
208 memcpy(&info, (void *) buf, MIN(size, expected_entry_size));
209
210 printf("{op=" XLAT_FMT, XLAT_ARGS(PTRACE_SYSCALL_INFO_ENTRY));
211 if (info.op != PTRACE_SYSCALL_INFO_ENTRY)
212 FAIL("#%d: entry stop mismatch", ptrace_stop);
213
214 if (size < offsetofend(struct_ptrace_syscall_info, arch))
215 goto printed_entry_common;
216 printf(", arch=");
217 printxval(audit_arch, info.arch, "AUDIT_ARCH_???");
218 if (!info.arch)
219 FAIL("#%d: entry stop mismatch", ptrace_stop);
220
221 if (size < offsetofend(struct_ptrace_syscall_info,
222 instruction_pointer))
223 goto printed_entry_common;
224 printf(", instruction_pointer=%#llx",
225 (unsigned long long) info.instruction_pointer);
226 if (!info.instruction_pointer)
227 FAIL("#%d: entry stop mismatch", ptrace_stop);
228
229 if (size < offsetofend(struct_ptrace_syscall_info,
230 stack_pointer))
231 goto printed_entry_common;
232 printf(", stack_pointer=%#llx",
233 (unsigned long long) info.stack_pointer);
234 if (!info.stack_pointer)
235 FAIL("#%d: entry stop mismatch", ptrace_stop);
236
237 if (size < offsetofend(struct_ptrace_syscall_info, entry.nr))
238 goto printed_entry_common;
239 printf(", entry={nr="
240 NABBR("%llu") VERB(" /* ") NRAW("__NR_%s") VERB(" */"),
241 XLAT_SEL((unsigned long long) info.entry.nr,
242 sc_names[ptrace_stop / 2]));
243 const unsigned long *exp_args = args[ptrace_stop / 2];
244 if (info.entry.nr != exp_args[0])
245 FAIL("#%d: entry stop mismatch", ptrace_stop);
246
247 for (unsigned int i = 0; i < ARRAY_SIZE(info.entry.args); ++i) {
248 const unsigned int i_size =
249 offsetofend(struct_ptrace_syscall_info,
250 entry.args[i]);
251 if (size < i_size) {
252 if (i)
253 break;
254 goto printed_entry_nr;
255 }
256 #if SIZEOF_KERNEL_LONG_T > SIZEOF_LONG
257 # define CAST (unsigned long)
258 #else
259 # define CAST
260 #endif
261 printf("%s%#llx", (i ? ", " : ", args=["),
262 (unsigned long long) info.entry.args[i]);
263 if (CAST info.entry.args[i] != exp_args[i + 1])
264 FAIL("#%d: entry stop mismatch", ptrace_stop);
265 }
266 printf("]");
267
268 printed_entry_nr:
269 printf("}");
270
271 printed_entry_common:
272 printf("}) = %s\n", errstr);
273 }
274 }
275
276 static void
277 test_exit(void)
278 {
279 for (unsigned int size = 0;
280 size <= sizeof(struct_ptrace_syscall_info); ++size) {
281 unsigned long buf = end_of_page - size;
282 memset((void *) buf, -1, size);
283
284 long rc = do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, size, buf);
285 printf("ptrace(" XLAT_FMT ", %d, %u, ",
286 XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, size);
287 if (rc < (long) expected_exit_size || size == 0)
288 printf("%#lx) = %s\n", buf, errstr);
289
290 if (rc < 0)
291 FAIL("PTRACE_GET_SYSCALL_INFO");
292 if (rc < (long) expected_exit_size)
293 FAIL("#%d: exit stop mismatch", ptrace_stop);
294 if (size == 0)
295 continue;
296
297 /* copy to a local structure to avoid unaligned access */
298 struct_ptrace_syscall_info info;
299 memcpy(&info, (void *) buf, MIN(size, expected_exit_size));
300
301 printf("{op=" XLAT_FMT, XLAT_ARGS(PTRACE_SYSCALL_INFO_EXIT));
302 if (info.op != PTRACE_SYSCALL_INFO_EXIT)
303 FAIL("#%d: exit stop mismatch", ptrace_stop);
304
305 if (size < offsetofend(struct_ptrace_syscall_info, arch))
306 goto printed_exit_common;
307 printf(", arch=");
308 printxval(audit_arch, info.arch, "AUDIT_ARCH_???");
309 if (!info.arch)
310 FAIL("#%d: exit stop mismatch", ptrace_stop);
311
312 if (size < offsetofend(struct_ptrace_syscall_info,
313 instruction_pointer))
314 goto printed_exit_common;
315 printf(", instruction_pointer=%#llx",
316 (unsigned long long) info.instruction_pointer);
317 if (!info.instruction_pointer)
318 FAIL("#%d: exit stop mismatch", ptrace_stop);
319
320 if (size < offsetofend(struct_ptrace_syscall_info,
321 stack_pointer))
322 goto printed_exit_common;
323 printf(", stack_pointer=%#llx",
324 (unsigned long long) info.stack_pointer);
325 if (!info.stack_pointer)
326 FAIL("#%d: exit stop mismatch", ptrace_stop);
327
328 const struct {
329 unsigned int is_error;
330 int rval;
331 const char *str;
332 } exit_param[] = {
333 { 1, -ENOENT, "-ENOENT" }, /* chdir */
334 { 0, pid, NULL } /* gettid */
335 }, *exp_param = &exit_param[ptrace_stop / 2 - 1];
336
337 if (size < offsetofend(struct_ptrace_syscall_info, exit.rval))
338 goto printed_exit_common;
339 if (size >= expected_exit_size && info.exit.is_error) {
340 printf(", exit={rval=" XLAT_FMT_D,
341 XLAT_SEL(exp_param->rval, exp_param->str));
342 } else {
343 printf(", exit={rval=%lld", (long long) info.exit.rval);
344 }
345 if (info.exit.rval != exp_param->rval)
346 FAIL("#%d: exit stop mismatch", ptrace_stop);
347
348 if (size >= expected_exit_size) {
349 printf(", is_error=%u",
350 (unsigned int) info.exit.is_error);
351 if (info.exit.is_error != exp_param->is_error)
352 FAIL("#%d: exit stop mismatch", ptrace_stop);
353 }
354
355 printf("}");
356
357 printed_exit_common:
358 printf("}) = %s\n", errstr);
359 }
360 }
361
362 int
363 main(void)
364 {
365 end_of_page = (unsigned long) tail_alloc(1) + 1;
366
367 pid = getpid();
368 do_ptrace(PTRACE_GET_SYSCALL_INFO, pid, 0, 0);
369 printf("ptrace(" XLAT_FMT ", %d, 0, NULL) = %s\n",
370 XLAT_ARGS(PTRACE_GET_SYSCALL_INFO), pid, errstr);
371
372 pid = fork();
373 if (pid < 0)
374 PFAIL("fork");
375
376 if (pid == 0) {
377 /* get the pid before PTRACE_TRACEME */
378 pid = getpid();
379 if (do_ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
380 /* exit with a nonzero exit status */
381 PFAIL("PTRACE_TRACEME");
382 }
383 kill(pid, SIGSTOP);
384 for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
385 syscall(args[i][0],
386 args[i][1], args[i][2], args[i][3],
387 args[i][4], args[i][5], args[i][6]);
388 }
389 /* unreachable */
390 _exit(1);
391 }
392
393 for (ptrace_stop = 0; ; ++ptrace_stop) {
394 int status;
395 long rc = waitpid(pid, &status, 0);
396 if (rc != pid) {
397 /* cannot happen */
398 PFAIL("#%d: unexpected wait result %ld",
399 ptrace_stop, rc);
400 }
401 if (WIFEXITED(status)) {
402 /* tracee is no more */
403 pid = 0;
404 if (WEXITSTATUS(status) == 0)
405 break;
406 FAIL("#%d: unexpected exit status %u",
407 ptrace_stop, WEXITSTATUS(status));
408 }
409 if (WIFSIGNALED(status)) {
410 /* tracee is no more */
411 pid = 0;
412 FAIL("#%d: unexpected signal %u",
413 ptrace_stop, WTERMSIG(status));
414 }
415 if (!WIFSTOPPED(status)) {
416 /* cannot happen */
417 FAIL("#%d: unexpected wait status %#x",
418 ptrace_stop, status);
419 }
420
421 switch (WSTOPSIG(status)) {
422 case SIGSTOP:
423 if (ptrace_stop)
424 FAIL("#%d: unexpected signal stop",
425 ptrace_stop);
426 if (do_ptrace(PTRACE_SETOPTIONS, pid, 0,
427 PTRACE_O_TRACESYSGOOD) < 0) {
428 /* cannot happen */
429 PFAIL("PTRACE_SETOPTIONS");
430 }
431 printf("ptrace(" XLAT_FMT ", %d, NULL, " XLAT_FMT
432 ") = 0\n",
433 XLAT_ARGS(PTRACE_SETOPTIONS), pid,
434 XLAT_ARGS(PTRACE_O_TRACESYSGOOD));
435
436 if (!test_none())
437 goto done;
438 break;
439
440 case SIGTRAP | 0x80:
441 switch (ptrace_stop) {
442 case 1: /* entering chdir */
443 case 3: /* entering gettid */
444 case 5: /* entering exit_group */
445 test_entry();
446 break;
447 case 2: /* exiting chdir */
448 case 4: /* exiting gettid */
449 test_exit();
450 break;
451 default:
452 FAIL("#%d: unexpected syscall stop",
453 ptrace_stop);
454 }
455 break;
456
457 default:
458 FAIL("#%d: unexpected stop signal %#x",
459 ptrace_stop, WSTOPSIG(status));
460 }
461
462 if (do_ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0) {
463 /* cannot happen */
464 PFAIL("PTRACE_SYSCALL");
465 }
466 printf("ptrace(" XLAT_FMT ", %d, NULL, 0) = 0\n",
467 XLAT_ARGS(PTRACE_SYSCALL), pid);
468 }
469
470 done:
471 if (pid) {
472 kill_tracee();
473 waitpid(pid, NULL, 0);
474 }
475
476 puts("+++ exited with 0 +++");
477 return 0;
478 }