]> granicus.if.org Git - strace/blob - ptrace_syscall_info.c
tests: fix format warnings on x32
[strace] / ptrace_syscall_info.c
1 /*
2  * Copyright (c) 2018 Dmitry V. Levin <ldv@altlinux.org>
3  * Copyright (c) 2018-2019 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 "print_fields.h"
12 #include "ptrace.h"
13 #include "ptrace_syscall_info.h"
14 #include "scno.h"
15
16 #include <signal.h>
17 #include <sys/wait.h>
18
19 #include "xlat/ptrace_syscall_info_op.h"
20
21 bool ptrace_get_syscall_info_supported;
22
23 static int
24 kill_tracee(pid_t pid)
25 {
26         return kill_save_errno(pid, SIGKILL);
27 }
28
29 #define FAIL    do { ptrace_stop = -1U; goto done; } while (0)
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 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);
39
40 /*
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.
43  */
44 bool
45 test_ptrace_get_syscall_info(void)
46 {
47         /*
48          * NOMMU provides no forks necessary for PTRACE_GET_SYSCALL_INFO test,
49          * leave the default unchanged.
50          */
51 #ifdef HAVE_FORK
52         static const unsigned long args[][7] = {
53                 /* a sequence of architecture-agnostic syscalls */
54                 {
55                         __NR_chdir,
56                         (unsigned long) "",
57                         0xbad1fed1,
58                         0xbad2fed2,
59                         0xbad3fed3,
60                         0xbad4fed4,
61                         0xbad5fed5
62                 },
63                 {
64                         __NR_gettid,
65                         0xcaf0bea0,
66                         0xcaf1bea1,
67                         0xcaf2bea2,
68                         0xcaf3bea3,
69                         0xcaf4bea4,
70                         0xcaf5bea5
71                 },
72                 {
73                         __NR_exit_group,
74                         0,
75                         0xfac1c0d1,
76                         0xfac2c0d2,
77                         0xfac3c0d3,
78                         0xfac4c0d4,
79                         0xfac5c0d5
80                 }
81         };
82         const unsigned long *exp_args;
83
84         int pid = fork();
85         if (pid < 0)
86                 perror_func_msg_and_die("fork");
87
88         if (pid == 0) {
89                 /* get the pid before PTRACE_TRACEME */
90                 pid = getpid();
91                 if (ptrace(PTRACE_TRACEME, 0L, 0L, 0L) < 0) {
92                         /* exit with a nonzero exit status */
93                         perror_func_msg_and_die("PTRACE_TRACEME");
94                 }
95                 kill(pid, SIGSTOP);
96                 for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
97                         syscall(args[i][0],
98                                 args[i][1], args[i][2], args[i][3],
99                                 args[i][4], args[i][5], args[i][6]);
100                 }
101                 /* unreachable */
102                 _exit(1);
103         }
104
105         const struct {
106                 unsigned int is_error;
107                 int rval;
108         } *exp_param, exit_param[] = {
109                 { 1, -ENOENT }, /* chdir */
110                 { 0, pid }      /* gettid */
111         };
112
113         unsigned int ptrace_stop;
114
115         for (ptrace_stop = 0; ; ++ptrace_stop) {
116                 struct ptrace_syscall_info info = {
117                         .op = 0xff      /* invalid PTRACE_SYSCALL_INFO_* op */
118                 };
119                 const size_t size = sizeof(info);
120                 int status;
121                 long rc = waitpid(pid, &status, 0);
122                 if (rc != pid) {
123                         /* cannot happen */
124                         kill_tracee(pid);
125                         perror_func_msg_and_die("#%d: unexpected wait result"
126                                                 " %ld", ptrace_stop, rc);
127                 }
128                 if (WIFEXITED(status)) {
129                         /* tracee is no more */
130                         pid = 0;
131                         if (WEXITSTATUS(status) == 0)
132                                 break;
133                         debug_func_msg("#%d: unexpected exit status %u",
134                                        ptrace_stop, WEXITSTATUS(status));
135                         FAIL;
136                 }
137                 if (WIFSIGNALED(status)) {
138                         /* tracee is no more */
139                         pid = 0;
140                         debug_func_msg("#%d: unexpected signal %u",
141                                        ptrace_stop, WTERMSIG(status));
142                         FAIL;
143                 }
144                 if (!WIFSTOPPED(status)) {
145                         /* cannot happen */
146                         kill_tracee(pid);
147                         error_func_msg_and_die("#%d: unexpected wait status"
148                                                " %#x", ptrace_stop, status);
149                 }
150
151                 switch (WSTOPSIG(status)) {
152                 case SIGSTOP:
153                         if (ptrace_stop) {
154                                 debug_func_msg("#%d: unexpected signal stop",
155                                                ptrace_stop);
156                                 FAIL;
157                         }
158                         if (ptrace(PTRACE_SETOPTIONS, pid, 0L,
159                                    PTRACE_O_TRACESYSGOOD) < 0) {
160                                 /* cannot happen */
161                                 kill_tracee(pid);
162                                 perror_func_msg_and_die("PTRACE_SETOPTIONS");
163                         }
164                         rc = ptrace(PTRACE_GET_SYSCALL_INFO, pid,
165                                     (void *) size, &info);
166                         if (rc < 0) {
167                                 debug_perror_msg("PTRACE_GET_SYSCALL_INFO");
168                                 FAIL;
169                         }
170                         if (rc < (long) expected_none_size
171                             || info.op != PTRACE_SYSCALL_INFO_NONE
172                             || !info.arch
173                             || !info.instruction_pointer
174                             || !info.stack_pointer) {
175                                 debug_func_msg("signal stop mismatch");
176                                 FAIL;
177                         }
178                         break;
179
180                 case SIGTRAP | 0x80:
181                         rc = ptrace(PTRACE_GET_SYSCALL_INFO, pid,
182                                     (void *) size, &info);
183                         if (rc < 0) {
184                                 debug_perror_msg("#%d: PTRACE_GET_SYSCALL_INFO",
185                                                  ptrace_stop);
186                                 FAIL;
187                         }
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
195                                     || !info.arch
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"
206                                                        " mismatch",
207                                                        ptrace_stop);
208                                         FAIL;
209                                 }
210                                 break;
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
216                                     || !info.arch
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"
222                                                        " mismatch",
223                                                        ptrace_stop);
224                                         FAIL;
225                                 }
226                                 break;
227                         default:
228                                 debug_func_msg("#%d: unexpected syscall stop",
229                                                ptrace_stop);
230                                 FAIL;
231                         }
232                         break;
233
234                 default:
235                         debug_func_msg("#%d: unexpected stop signal %#x",
236                                        ptrace_stop, WSTOPSIG(status));
237                         FAIL;
238                 }
239
240                 if (ptrace(PTRACE_SYSCALL, pid, 0L, 0L) < 0) {
241                         /* cannot happen */
242                         kill_tracee(pid);
243                         perror_func_msg_and_die("PTRACE_SYSCALL");
244                 }
245         }
246
247 done:
248         if (pid) {
249                 kill_tracee(pid);
250                 waitpid(pid, NULL, 0);
251                 ptrace_stop = -1U;
252         }
253
254         ptrace_get_syscall_info_supported =
255                 ptrace_stop == ARRAY_SIZE(args) * 2;
256
257         if (ptrace_get_syscall_info_supported)
258                 debug_msg("PTRACE_GET_SYSCALL_INFO works");
259         else
260                 debug_msg("PTRACE_GET_SYSCALL_INFO does not work");
261 #endif /* HAVE_FORK */
262
263         return ptrace_get_syscall_info_supported;
264 }
265
266 void
267 print_ptrace_syscall_info(struct tcb *tcp, kernel_ulong_t addr,
268                           kernel_ulong_t user_len)
269 {
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);
274
275         if (!fetch_size || !tfetch_mem(tcp, addr, fetch_size, &info)) {
276                 printaddr(addr);
277                 return;
278         }
279
280         PRINT_FIELD_XVAL("{", info, op, ptrace_syscall_info_op,
281                          "PTRACE_SYSCALL_INFO_???");
282         if (fetch_size < offsetofend(struct ptrace_syscall_info, arch))
283                 goto printed;
284         PRINT_FIELD_XVAL(", ", info, arch, audit_arch, "AUDIT_ARCH_???");
285
286         if (fetch_size < offsetofend(struct ptrace_syscall_info,
287                                      instruction_pointer))
288                 goto printed;
289         PRINT_FIELD_ADDR64(", ", info, instruction_pointer);
290
291         if (fetch_size < offsetofend(struct ptrace_syscall_info, stack_pointer))
292                 goto printed;
293         PRINT_FIELD_ADDR64(", ", info, stack_pointer);
294
295         if (fetch_size < offsetofend(struct ptrace_syscall_info, entry.nr))
296                 goto printed;
297
298         switch(info.op) {
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={"),
303                                       info.entry, nr);
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,
308                                                     entry.args[i]);
309                                 if (fetch_size < i_size) {
310                                         if (i)
311                                                 break;
312                                         goto entry_printed;
313                                 }
314                                 tprintf(", %s%#" PRIx64,
315                                         (i ? "" : "arg=["),
316                                         (uint64_t) info.entry.args[i]);
317                         }
318                         tprints("]");
319                         if (info.op == PTRACE_SYSCALL_INFO_SECCOMP
320                             && fetch_size >= expected_seccomp_size)
321                                 PRINT_FIELD_U(", ", info.seccomp, ret_data);
322 entry_printed:
323                         tprints("}");
324                         break;
325                 case PTRACE_SYSCALL_INFO_EXIT:
326                         tprints(", exit={");
327                         if (fetch_size >= expected_exit_size
328                             && info.exit.is_error) {
329                                 PRINT_FIELD_ERR_D("", info.exit, rval);
330                         } else {
331                                 PRINT_FIELD_D("", info.exit, rval);
332                         }
333                         if (fetch_size >= expected_exit_size)
334                                 PRINT_FIELD_U(", ", info.exit, is_error);
335                         tprints("}");
336                         break;
337         }
338
339 printed:
340         tprints("}");
341 }