]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/test/ptrace/scescx.c
Upgrade to OpenSSH 7.5p1.
[FreeBSD/FreeBSD.git] / tools / test / ptrace / scescx.c
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$");
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 *
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 *
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 *
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
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
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                                 for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
200                                     i++) {
201                                         printf("%c%#lx", i == 0 ? '(' : ',',
202                                             args[i]);
203                                 }
204                         } else {
205                                 fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
206                                     strerror(errno));
207                         }
208                         printf(")");
209                         free(args);
210                 }
211         }
212         printf("\n");
213 }
214
215 static int
216 trace_sc(int pid)
217 {
218         struct ptrace_lwpinfo lwpinfo;
219         int status;
220
221         if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
222                 perror("PT_TO_SCE");
223                 ptrace(PT_KILL, pid, NULL, 0);
224                 return (-1);
225         }
226
227         if (waitpid(pid, &status, 0) == -1) {
228                 perror("waitpid");
229                 return (-1);
230         }
231         if (WIFEXITED(status) || WIFSIGNALED(status)) {
232                 wait_info(pid, status, NULL);
233                 return (-1);
234         }
235         assert(WIFSTOPPED(status));
236         assert(WSTOPSIG(status) == SIGTRAP);
237
238         if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
239                 perror("PT_LWPINFO");
240                 ptrace(PT_KILL, pid, NULL, 0);
241                 return (-1);
242         }
243         wait_info(pid, status, &lwpinfo);
244         assert(lwpinfo.pl_flags & PL_FLAG_SCE);
245
246         if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
247                 perror("PT_TO_SCX");
248                 ptrace(PT_KILL, pid, NULL, 0);
249                 return (-1);
250         }
251
252         if (waitpid(pid, &status, 0) == -1) {
253                 perror("waitpid");
254                 return (-1);
255         }
256         if (WIFEXITED(status) || WIFSIGNALED(status)) {
257                 wait_info(pid, status, NULL);
258                 return (-1);
259         }
260         assert(WIFSTOPPED(status));
261         assert(WSTOPSIG(status) == SIGTRAP);
262
263         if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
264                 perror("PT_LWPINFO");
265                 ptrace(PT_KILL, pid, NULL, 0);
266                 return (-1);
267         }
268         wait_info(pid, status, &lwpinfo);
269         assert(lwpinfo.pl_flags & PL_FLAG_SCX);
270
271         if (lwpinfo.pl_flags & PL_FLAG_EXEC)
272                 get_pathname(pid);
273
274         if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
275                 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
276                 return (lwpinfo.pl_child_pid);
277         }
278         return (0);
279 }
280
281 static int
282 trace_cont(int pid)
283 {
284         struct ptrace_lwpinfo lwpinfo;
285         int status;
286
287         if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
288                 perror("PT_CONTINUE");
289                 ptrace(PT_KILL, pid, NULL, 0);
290                 return (-1);
291         }
292
293         if (waitpid(pid, &status, 0) == -1) {
294                 perror("waitpid");
295                 return (-1);
296         }
297         if (WIFEXITED(status) || WIFSIGNALED(status)) {
298                 wait_info(pid, status, NULL);
299                 return (-1);
300         }
301         assert(WIFSTOPPED(status));
302         assert(WSTOPSIG(status) == SIGTRAP);
303
304         if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
305                 perror("PT_LWPINFO");
306                 ptrace(PT_KILL, pid, NULL, 0);
307                 return (-1);
308         }
309         wait_info(pid, status, &lwpinfo);
310
311         if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
312             (PL_FLAG_EXEC | PL_FLAG_SCX))
313                 get_pathname(pid);
314
315         if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
316             (PL_FLAG_FORKED | PL_FLAG_SCX)) {
317                 printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
318                 return (lwpinfo.pl_child_pid);
319         }
320
321         return (0);
322 }
323
324 static int trace_syscalls = 1;
325
326 static int
327 trace(pid_t pid)
328 {
329
330         return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
331 }
332
333
334 int
335 main(int argc, char *argv[])
336 {
337         struct ptrace_lwpinfo lwpinfo;
338         int c, status, use_vfork;
339         pid_t pid, pid1;
340
341         trace_syscalls = 1;
342         use_vfork = 0;
343         while ((c = getopt(argc, argv, "csv")) != -1) {
344                 switch (c) {
345                 case 'c':
346                         trace_syscalls = 0;
347                         break;
348                 case 's':
349                         trace_syscalls = 1;
350                         break;
351                 case 'v':
352                         use_vfork = 1;
353                         break;
354                 default:
355                 case '?':
356                         fprintf(stderr, "Usage: %s [-c] [-s] [-v]\n", argv[0]);
357                         return (2);
358                 }
359         }
360
361         if ((pid = fork()) < 0) {
362                 perror("fork");
363                 return 1;
364         }
365         else if (pid == 0) {
366                 if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
367                         perror("PT_TRACE_ME");
368                         _exit(1);
369                 }
370                 kill(getpid(), SIGSTOP);
371                 getpid();
372                 if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
373                         perror("fork1");
374                         return (1);
375                 } else if (pid1 == 0) {
376                         printf("Hi from child %d\n", getpid());
377                         execl("/bin/ls", "ls", "/", (char *)NULL);
378                 }
379         }
380         else { /* parent */
381                 if (waitpid(pid, &status, 0) == -1) {
382                         perror("waitpid");
383                         return (-1);
384                 }
385                 assert(WIFSTOPPED(status));
386                 assert(WSTOPSIG(status) == SIGSTOP);
387
388                 if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
389                     sizeof(lwpinfo)) < 0) {
390                         perror("PT_LWPINFO");
391                         ptrace(PT_KILL, pid, NULL, 0);
392                         return (-1);
393                 }
394                 wait_info(pid, status, &lwpinfo);
395
396                 if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
397                         perror("PT_FOLLOW_FORK");
398                         ptrace(PT_KILL, pid, NULL, 0);
399                         return (2);
400                 }
401
402                 while ((pid1 = trace(pid)) >= 0) {
403                         if (pid1 != 0) {
404                                 printf(TRACE "attached to pid %d\n", pid1);
405 #if 0
406                                 kill(pid1, SIGCONT);
407 #endif
408                                 if (waitpid(pid1, &status, 0) == -1) {
409                                         perror("waitpid");
410                                         return (-1);
411                                 }
412                                 printf(TRACE "nested loop, pid %d status %s\n",
413                                     pid1, decode_wait_status(status));
414                                 assert(WIFSTOPPED(status));
415                                 assert(WSTOPSIG(status) == SIGSTOP);
416                                 if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
417                                     sizeof(lwpinfo)) < 0) {
418                                         perror("PT_LWPINFO");
419                                         ptrace(PT_KILL, pid1, NULL, 0);
420                                         return (-1);
421                                 }
422                                 wait_info(pid1, status, &lwpinfo);
423
424                                 while (trace(pid1) >= 0)
425                                         ;
426                         }
427                 }
428
429                 ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
430         }
431         return (0);
432 }