1 /*-
2 * Copyright (c) 2011, 2012 Konstantin Belousov <kib@FreeBSD.org>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD: stable/12/tools/test/ptrace/scescx.c 370623 2021-09-19 11:21:32Z kib $");
28
29 #include <sys/types.h>
30 #include <sys/ptrace.h>
31 #include <sys/sysctl.h>
32 #include <sys/wait.h>
33 #include <assert.h>
34 #include <errno.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #define TRACE ">>>> "
42
43 static const char *
decode_wait_status(int status)44 decode_wait_status(int status)
45 {
46 static char c[128];
47 char b[32];
48 int first;
49
50 c[0] = '\0';
51 first = 1;
52 if (WIFCONTINUED(status)) {
53 first = 0;
54 strlcat(c, "CONT", sizeof(c));
55 }
56 if (WIFEXITED(status)) {
57 if (first)
58 first = 0;
59 else
60 strlcat(c, ",", sizeof(c));
61 snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status));
62 strlcat(c, b, sizeof(c));
63 }
64 if (WIFSIGNALED(status)) {
65 if (first)
66 first = 0;
67 else
68 strlcat(c, ",", sizeof(c));
69 snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status)));
70 strlcat(c, b, sizeof(c));
71 if (WCOREDUMP(status))
72 strlcat(c, ",CORE", sizeof(c));
73 }
74 if (WIFSTOPPED(status)) {
75 if (first)
76 first = 0;
77 else
78 strlcat(c, ",", sizeof(c));
79 snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status)));
80 strlcat(c, b, sizeof(c));
81 }
82 return (c);
83 }
84
85 static const char *
decode_pl_flags(struct ptrace_lwpinfo * lwpinfo)86 decode_pl_flags(struct ptrace_lwpinfo *lwpinfo)
87 {
88 static char c[128];
89 static struct decode_tag {
90 int flag;
91 const char *desc;
92 } decode[] = {
93 { PL_FLAG_SA, "SA" },
94 { PL_FLAG_BOUND, "BOUND" },
95 { PL_FLAG_SCE, "SCE" },
96 { PL_FLAG_SCX, "SCX" },
97 { PL_FLAG_EXEC, "EXEC" },
98 { PL_FLAG_SI, "SI" },
99 { PL_FLAG_FORKED, "FORKED" },
100 { PL_FLAG_CHILD, "CHILD" },
101 { PL_FLAG_BORN, "LWPBORN" },
102 { PL_FLAG_EXITED, "LWPEXITED" },
103 { PL_FLAG_VFORKED, "VFORKED" },
104 { PL_FLAG_VFORK_DONE, "VFORKDONE" },
105 };
106 char de[32];
107 unsigned first, flags, i;
108
109 c[0] = '\0';
110 first = 1;
111 flags = lwpinfo->pl_flags;
112 for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
113 if ((flags & decode[i].flag) != 0) {
114 if (first)
115 first = 0;
116 else
117 strlcat(c, ",", sizeof(c));
118 strlcat(c, decode[i].desc, sizeof(c));
119 flags &= ~decode[i].flag;
120 }
121 }
122 for (i = 0; i < sizeof(flags) * NBBY; i++) {
123 if ((flags & (1 << i)) != 0) {
124 if (first)
125 first = 0;
126 else
127 strlcat(c, ",", sizeof(c));
128 snprintf(de, sizeof(de), "<%d>", i);
129 strlcat(c, de, sizeof(c));
130 }
131 }
132 return (c);
133 }
134
135 static const char *
decode_pl_event(struct ptrace_lwpinfo * lwpinfo)136 decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
137 {
138
139 switch (lwpinfo->pl_event) {
140 case PL_EVENT_NONE:
141 return ("NONE");
142
143 case PL_EVENT_SIGNAL:
144 return ("SIG");
145
146 default:
147 return ("UNKNOWN");
148 }
149 }
150
151 static void
get_pathname(pid_t pid)152 get_pathname(pid_t pid)
153 {
154 char pathname[PATH_MAX];
155 int error, name[4];
156 size_t len;
157
158 name[0] = CTL_KERN;
159 name[1] = KERN_PROC;
160 name[2] = KERN_PROC_PATHNAME;
161 name[3] = pid;
162
163 len = sizeof(pathname);
164 error = sysctl(name, 4, pathname, &len, NULL, 0);
165 if (error < 0) {
166 if (errno != ESRCH) {
167 fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
168 pid, strerror(errno));
169 return;
170 }
171 fprintf(stderr, "pid %d exited\n", pid);
172 return;
173 }
174 if (len == 0 || strlen(pathname) == 0) {
175 fprintf(stderr, "No cached pathname for process %d\n", pid);
176 return;
177 }
178 printf(TRACE "pid %d path %s\n", pid, pathname);
179 }
180
181 static void
wait_info(int pid,int status,struct ptrace_lwpinfo * lwpinfo)182 wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
183 {
184 long *args;
185 int error, i;
186
187 printf(TRACE "pid %d wait %s", pid,
188 decode_wait_status(status));
189 if (lwpinfo != NULL) {
190 printf(" event %s flags %s",
191 decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
192 if ((lwpinfo->pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) != 0) {
193 printf(" sc%d", lwpinfo->pl_syscall_code);
194 args = calloc(lwpinfo->pl_syscall_narg, sizeof(long));
195 error = ptrace(PT_GET_SC_ARGS, lwpinfo->pl_lwpid,
196 (caddr_t)args, lwpinfo->pl_syscall_narg *
197 sizeof(long));
198 if (error == 0) {
199 printf("(");
200 for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
201 i++) {
202 printf("%s%#lx", i == 0 ? "" : ",",
203 args[i]);
204 }
205 printf(")");
206 } else {
207 fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
208 strerror(errno));
209 }
210 free(args);
211 }
212 }
213 printf("\n");
214 }
215
216 static int
trace_sc(int pid)217 trace_sc(int pid)
218 {
219 struct ptrace_lwpinfo lwpinfo;
220 int status;
221
222 if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
223 perror("PT_TO_SCE");
224 ptrace(PT_KILL, pid, NULL, 0);
225 return (-1);
226 }
227
228 if (waitpid(pid, &status, 0) == -1) {
229 perror("waitpid");
230 return (-1);
231 }
232 if (WIFEXITED(status) || WIFSIGNALED(status)) {
233 wait_info(pid, status, NULL);
234 return (-1);
235 }
236 assert(WIFSTOPPED(status));
237 assert(WSTOPSIG(status) == SIGTRAP);
238
239 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
240 perror("PT_LWPINFO");
241 ptrace(PT_KILL, pid, NULL, 0);
242 return (-1);
243 }
244 wait_info(pid, status, &lwpinfo);
245 assert(lwpinfo.pl_flags & PL_FLAG_SCE);
246
247 if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
248 perror("PT_TO_SCX");
249 ptrace(PT_KILL, pid, NULL, 0);
250 return (-1);
251 }
252
253 if (waitpid(pid, &status, 0) == -1) {
254 perror("waitpid");
255 return (-1);
256 }
257 if (WIFEXITED(status) || WIFSIGNALED(status)) {
258 wait_info(pid, status, NULL);
259 return (-1);
260 }
261 assert(WIFSTOPPED(status));
262 assert(WSTOPSIG(status) == SIGTRAP);
263
264 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
265 perror("PT_LWPINFO");
266 ptrace(PT_KILL, pid, NULL, 0);
267 return (-1);
268 }
269 wait_info(pid, status, &lwpinfo);
270 assert(lwpinfo.pl_flags & PL_FLAG_SCX);
271
272 if (lwpinfo.pl_flags & PL_FLAG_EXEC)
273 get_pathname(pid);
274
275 if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
276 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
277 return (lwpinfo.pl_child_pid);
278 }
279 return (0);
280 }
281
282 static int
trace_cont(int pid)283 trace_cont(int pid)
284 {
285 struct ptrace_lwpinfo lwpinfo;
286 int status;
287
288 if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
289 perror("PT_CONTINUE");
290 ptrace(PT_KILL, pid, NULL, 0);
291 return (-1);
292 }
293
294 if (waitpid(pid, &status, 0) == -1) {
295 perror("waitpid");
296 return (-1);
297 }
298 if (WIFEXITED(status) || WIFSIGNALED(status)) {
299 wait_info(pid, status, NULL);
300 return (-1);
301 }
302 assert(WIFSTOPPED(status));
303 assert(WSTOPSIG(status) == SIGTRAP);
304
305 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
306 perror("PT_LWPINFO");
307 ptrace(PT_KILL, pid, NULL, 0);
308 return (-1);
309 }
310 wait_info(pid, status, &lwpinfo);
311
312 if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
313 (PL_FLAG_EXEC | PL_FLAG_SCX))
314 get_pathname(pid);
315
316 if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
317 (PL_FLAG_FORKED | PL_FLAG_SCX)) {
318 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
319 return (lwpinfo.pl_child_pid);
320 }
321
322 return (0);
323 }
324
325 static int trace_syscalls = 1;
326
327 static int
trace(pid_t pid)328 trace(pid_t pid)
329 {
330
331 return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
332 }
333
334
335 int
main(int argc,char * argv[])336 main(int argc, char *argv[])
337 {
338 struct ptrace_lwpinfo lwpinfo;
339 int c, status, use_vfork;
340 pid_t pid, pid1;
341
342 trace_syscalls = 1;
343 use_vfork = 0;
344 while ((c = getopt(argc, argv, "csv")) != -1) {
345 switch (c) {
346 case 'c':
347 trace_syscalls = 0;
348 break;
349 case 's':
350 trace_syscalls = 1;
351 break;
352 case 'v':
353 use_vfork = 1;
354 break;
355 default:
356 case '?':
357 fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
358 return (2);
359 }
360 }
361
362 if ((pid = fork()) < 0) {
363 perror("fork");
364 return 1;
365 }
366 else if (pid == 0) {
367 if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
368 perror("PT_TRACE_ME");
369 _exit(1);
370 }
371 kill(getpid(), SIGSTOP);
372 getpid();
373 if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
374 perror("fork1");
375 return (1);
376 } else if (pid1 == 0) {
377 printf("Hi from child %d\n", getpid());
378 execl("/bin/ls", "ls", "/", (char *)NULL);
379 }
380 }
381 else { /* parent */
382 if (waitpid(pid, &status, 0) == -1) {
383 perror("waitpid");
384 return (-1);
385 }
386 assert(WIFSTOPPED(status));
387 assert(WSTOPSIG(status) == SIGSTOP);
388
389 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
390 sizeof(lwpinfo)) < 0) {
391 perror("PT_LWPINFO");
392 ptrace(PT_KILL, pid, NULL, 0);
393 return (-1);
394 }
395 wait_info(pid, status, &lwpinfo);
396
397 if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
398 perror("PT_FOLLOW_FORK");
399 ptrace(PT_KILL, pid, NULL, 0);
400 return (2);
401 }
402
403 while ((pid1 = trace(pid)) >= 0) {
404 if (pid1 != 0) {
405 printf(TRACE "attached to pid %d\n", pid1);
406 #if 0
407 kill(pid1, SIGCONT);
408 #endif
409 if (waitpid(pid1, &status, 0) == -1) {
410 perror("waitpid");
411 return (-1);
412 }
413 printf(TRACE "nested loop, pid %d status %s\n",
414 pid1, decode_wait_status(status));
415 assert(WIFSTOPPED(status));
416 assert(WSTOPSIG(status) == SIGSTOP);
417 if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
418 sizeof(lwpinfo)) < 0) {
419 perror("PT_LWPINFO");
420 ptrace(PT_KILL, pid1, NULL, 0);
421 return (-1);
422 }
423 wait_info(pid1, status, &lwpinfo);
424
425 while (trace(pid1) >= 0)
426 ;
427 }
428 }
429
430 ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
431 }
432 return (0);
433 }
434