xref: /freebsd/tools/test/ptrace/scescx.c (revision 6bfca4dc)
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/types.h>
27 #include <sys/ptrace.h>
28 #include <sys/syscall.h>
29 #include <sys/sysctl.h>
30 #include <sys/wait.h>
31 #include <assert.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #define TRACE	">>>> "
40 
41 static const char *
decode_wait_status(int status)42 decode_wait_status(int status)
43 {
44 	static char c[128];
45 	char b[32];
46 	int first;
47 
48 	c[0] = '\0';
49 	first = 1;
50 	if (WIFCONTINUED(status)) {
51 		first = 0;
52 		strlcat(c, "CONT", sizeof(c));
53 	}
54 	if (WIFEXITED(status)) {
55 		if (first)
56 			first = 0;
57 		else
58 			strlcat(c, ",", sizeof(c));
59 		snprintf(b, sizeof(b), "EXIT(%d)", WEXITSTATUS(status));
60 		strlcat(c, b, sizeof(c));
61 	}
62 	if (WIFSIGNALED(status)) {
63 		if (first)
64 			first = 0;
65 		else
66 			strlcat(c, ",", sizeof(c));
67 		snprintf(b, sizeof(b), "SIG(%s)", strsignal(WTERMSIG(status)));
68 		strlcat(c, b, sizeof(c));
69 		if (WCOREDUMP(status))
70 			strlcat(c, ",CORE", sizeof(c));
71 	}
72 	if (WIFSTOPPED(status)) {
73 		if (first)
74 			first = 0;
75 		else
76 			strlcat(c, ",", sizeof(c));
77 		snprintf(b, sizeof(b), "SIG(%s)", strsignal(WSTOPSIG(status)));
78 		strlcat(c, b, sizeof(c));
79 	}
80 	return (c);
81 }
82 
83 static const char *
decode_pl_flags(struct ptrace_lwpinfo * lwpinfo)84 decode_pl_flags(struct ptrace_lwpinfo *lwpinfo)
85 {
86 	static char c[128];
87 	static struct decode_tag {
88 		int flag;
89 		const char *desc;
90 	} decode[] = {
91 		{ PL_FLAG_SA, "SA" },
92 		{ PL_FLAG_BOUND, "BOUND" },
93 		{ PL_FLAG_SCE, "SCE" },
94 		{ PL_FLAG_SCX, "SCX" },
95 		{ PL_FLAG_EXEC, "EXEC" },
96 		{ PL_FLAG_SI, "SI" },
97 		{ PL_FLAG_FORKED, "FORKED" },
98 		{ PL_FLAG_CHILD, "CHILD" },
99 		{ PL_FLAG_BORN, "LWPBORN" },
100 		{ PL_FLAG_EXITED, "LWPEXITED" },
101 		{ PL_FLAG_VFORKED, "VFORKED" },
102 		{ PL_FLAG_VFORK_DONE, "VFORKDONE" },
103 	};
104 	char de[32];
105 	unsigned first, flags, i;
106 
107 	c[0] = '\0';
108 	first = 1;
109 	flags = lwpinfo->pl_flags;
110 	for (i = 0; i < sizeof(decode) / sizeof(decode[0]); i++) {
111 		if ((flags & decode[i].flag) != 0) {
112 			if (first)
113 				first = 0;
114 			else
115 				strlcat(c, ",", sizeof(c));
116 			strlcat(c, decode[i].desc, sizeof(c));
117 			flags &= ~decode[i].flag;
118 		}
119 	}
120 	for (i = 0; i < sizeof(flags) * NBBY; i++) {
121 		if ((flags & (1 << i)) != 0) {
122 			if (first)
123 				first = 0;
124 			else
125 				strlcat(c, ",", sizeof(c));
126 			snprintf(de, sizeof(de), "<%d>", i);
127 			strlcat(c, de, sizeof(c));
128 		}
129 	}
130 	return (c);
131 }
132 
133 static const char *
decode_pl_event(struct ptrace_lwpinfo * lwpinfo)134 decode_pl_event(struct ptrace_lwpinfo *lwpinfo)
135 {
136 
137 	switch (lwpinfo->pl_event) {
138 	case PL_EVENT_NONE:
139 		return ("NONE");
140 
141 	case PL_EVENT_SIGNAL:
142 		return ("SIG");
143 
144 	default:
145 		return ("UNKNOWN");
146 	}
147 }
148 
149 static void
get_pathname(pid_t pid)150 get_pathname(pid_t pid)
151 {
152 	char pathname[PATH_MAX];
153 	int error, name[4];
154 	size_t len;
155 
156 	name[0] = CTL_KERN;
157 	name[1] = KERN_PROC;
158 	name[2] = KERN_PROC_PATHNAME;
159 	name[3] = pid;
160 
161 	len = sizeof(pathname);
162 	error = sysctl(name, 4, pathname, &len, NULL, 0);
163 	if (error < 0) {
164 		if (errno != ESRCH) {
165 			fprintf(stderr, "sysctl kern.proc.pathname.%d: %s\n",
166 			    pid, strerror(errno));
167 			return;
168 		}
169 		fprintf(stderr, "pid %d exited\n", pid);
170 		return;
171 	}
172 	if (len == 0 || strlen(pathname) == 0) {
173 		fprintf(stderr, "No cached pathname for process %d\n", pid);
174 		return;
175 	}
176 	printf(TRACE "pid %d path %s\n", pid, pathname);
177 }
178 
179 static void
wait_info(int pid,int status,struct ptrace_lwpinfo * lwpinfo)180 wait_info(int pid, int status, struct ptrace_lwpinfo *lwpinfo)
181 {
182 	long *args;
183 	int error, i;
184 
185 	printf(TRACE "pid %d wait %s", pid,
186 	    decode_wait_status(status));
187 	if (lwpinfo != NULL) {
188 		printf(" event %s flags %s",
189 		    decode_pl_event(lwpinfo), decode_pl_flags(lwpinfo));
190 		if ((lwpinfo->pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) != 0) {
191 			printf(" sc%d", lwpinfo->pl_syscall_code);
192 			args = calloc(lwpinfo->pl_syscall_narg, sizeof(long));
193 			error = ptrace(PT_GET_SC_ARGS, lwpinfo->pl_lwpid,
194 			    (caddr_t)args, lwpinfo->pl_syscall_narg *
195 			    sizeof(long));
196 			if (error == 0) {
197 				printf("(");
198 				for (i = 0; i < (int)lwpinfo->pl_syscall_narg;
199 				    i++) {
200 					printf("%s%#lx", i == 0 ? "" : ",",
201 					    args[i]);
202 				}
203 				printf(")");
204 			} else {
205 				fprintf(stderr, "PT_GET_SC_ARGS failed: %s",
206 				    strerror(errno));
207 			}
208 			free(args);
209 		}
210 	}
211 	printf("\n");
212 }
213 
214 static int trace_syscalls = 1;
215 static int remote_getpid = 0;
216 
217 static int
trace_sc(int pid)218 trace_sc(int pid)
219 {
220 	struct ptrace_sc_remote pscr;
221 	struct ptrace_lwpinfo lwpinfo;
222 	int status;
223 
224 	if (ptrace(PT_TO_SCE, pid, (caddr_t)1, 0) < 0) {
225 		perror("PT_TO_SCE");
226 		ptrace(PT_KILL, pid, NULL, 0);
227 		return (-1);
228 	}
229 
230 	if (waitpid(pid, &status, 0) == -1) {
231 		perror("waitpid");
232 		return (-1);
233 	}
234 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
235 		wait_info(pid, status, NULL);
236 		return (-1);
237 	}
238 	assert(WIFSTOPPED(status));
239 	assert(WSTOPSIG(status) == SIGTRAP);
240 
241 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
242 		perror("PT_LWPINFO");
243 		ptrace(PT_KILL, pid, NULL, 0);
244 		return (-1);
245 	}
246 	wait_info(pid, status, &lwpinfo);
247 	assert(lwpinfo.pl_flags & PL_FLAG_SCE);
248 
249 	if (ptrace(PT_TO_SCX, pid, (caddr_t)1, 0) < 0) {
250 		perror("PT_TO_SCX");
251 		ptrace(PT_KILL, pid, NULL, 0);
252 		return (-1);
253 	}
254 
255 	if (waitpid(pid, &status, 0) == -1) {
256 		perror("waitpid");
257 		return (-1);
258 	}
259 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
260 		wait_info(pid, status, NULL);
261 		return (-1);
262 	}
263 	assert(WIFSTOPPED(status));
264 	assert(WSTOPSIG(status) == SIGTRAP);
265 
266 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
267 		perror("PT_LWPINFO");
268 		ptrace(PT_KILL, pid, NULL, 0);
269 		return (-1);
270 	}
271 	wait_info(pid, status, &lwpinfo);
272 	assert(lwpinfo.pl_flags & PL_FLAG_SCX);
273 
274 	if (remote_getpid) {
275 		memset(&pscr, 0, sizeof(pscr));
276 		pscr.pscr_syscall = SYS_getpid;
277 		pscr.pscr_nargs = 0;
278 		if (ptrace(PT_SC_REMOTE, pid, (caddr_t)&pscr,
279 		    sizeof(pscr)) < 0) {
280 			perror("PT_SC_REMOTE");
281 			ptrace(PT_KILL, pid, NULL, 0);
282 			return (-1);
283 		} else {
284 			printf(TRACE "remote getpid %ld errno %d\n",
285 			    pscr.pscr_ret.sr_retval[0], pscr.pscr_ret.sr_error);
286 			if (waitpid(pid, &status, 0) == -1) {
287 			  perror("waitpid");
288 			  return (-1);
289 			}
290 		}
291 	}
292 	if (lwpinfo.pl_flags & PL_FLAG_EXEC)
293 		get_pathname(pid);
294 
295 	if (lwpinfo.pl_flags & PL_FLAG_FORKED) {
296 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
297 		return (lwpinfo.pl_child_pid);
298 	}
299 	return (0);
300 }
301 
302 static int
trace_cont(int pid)303 trace_cont(int pid)
304 {
305 	struct ptrace_lwpinfo lwpinfo;
306 	int status;
307 
308 	if (ptrace(PT_CONTINUE, pid, (caddr_t)1, 0) < 0) {
309 		perror("PT_CONTINUE");
310 		ptrace(PT_KILL, pid, NULL, 0);
311 		return (-1);
312 	}
313 
314 	if (waitpid(pid, &status, 0) == -1) {
315 		perror("waitpid");
316 		return (-1);
317 	}
318 	if (WIFEXITED(status) || WIFSIGNALED(status)) {
319 		wait_info(pid, status, NULL);
320 		return (-1);
321 	}
322 	assert(WIFSTOPPED(status));
323 	assert(WSTOPSIG(status) == SIGTRAP);
324 
325 	if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) < 0) {
326 		perror("PT_LWPINFO");
327 		ptrace(PT_KILL, pid, NULL, 0);
328 		return (-1);
329 	}
330 	wait_info(pid, status, &lwpinfo);
331 
332 	if ((lwpinfo.pl_flags & (PL_FLAG_EXEC | PL_FLAG_SCX)) ==
333 	    (PL_FLAG_EXEC | PL_FLAG_SCX))
334 		get_pathname(pid);
335 
336 	if ((lwpinfo.pl_flags & (PL_FLAG_FORKED | PL_FLAG_SCX)) ==
337 	    (PL_FLAG_FORKED | PL_FLAG_SCX)) {
338 		printf(TRACE "forked child %d\n", lwpinfo.pl_child_pid);
339 		return (lwpinfo.pl_child_pid);
340 	}
341 
342 	return (0);
343 }
344 
345 static int
trace(pid_t pid)346 trace(pid_t pid)
347 {
348 
349 	return (trace_syscalls ? trace_sc(pid) : trace_cont(pid));
350 }
351 
352 
353 int
main(int argc,char * argv[])354 main(int argc, char *argv[])
355 {
356 	struct ptrace_lwpinfo lwpinfo;
357 	int c, status, use_vfork;
358 	pid_t pid, pid1;
359 
360 	trace_syscalls = 1;
361 	remote_getpid = 0;
362 	use_vfork = 0;
363 	while ((c = getopt(argc, argv, "crsv")) != -1) {
364 		switch (c) {
365 		case 'c':
366 			trace_syscalls = 0;
367 			break;
368 		case 'r':
369 			remote_getpid = 1;
370 			break;
371 		case 's':
372 			trace_syscalls = 1;
373 			break;
374 		case 'v':
375 			use_vfork = 1;
376 			break;
377 		default:
378 		case '?':
379 			fprintf(stderr, "Usage: %s [-c] [-r] [-s] [-v]\n",
380 			    argv[0]);
381 			return (2);
382 		}
383 	}
384 
385 	if ((pid = fork()) < 0) {
386 		perror("fork");
387 		return 1;
388 	}
389 	else if (pid == 0) {
390 		if (ptrace(PT_TRACE_ME, 0, NULL, 0) < 0) {
391 			perror("PT_TRACE_ME");
392 			_exit(1);
393 		}
394 		kill(getpid(), SIGSTOP);
395 		getpid();
396 		if ((pid1 = use_vfork ? vfork() : fork()) < 0) {
397 			perror("fork1");
398 			return (1);
399 		} else if (pid1 == 0) {
400 			printf("Hi from child %d\n", getpid());
401 			execl("/bin/ls", "ls", "/", (char *)NULL);
402 		}
403 	}
404 	else { /* parent */
405 		if (waitpid(pid, &status, 0) == -1) {
406 			perror("waitpid");
407 			return (-1);
408 		}
409 		assert(WIFSTOPPED(status));
410 		assert(WSTOPSIG(status) == SIGSTOP);
411 
412 		if (ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo,
413 		    sizeof(lwpinfo)) < 0) {
414 			perror("PT_LWPINFO");
415 			ptrace(PT_KILL, pid, NULL, 0);
416 			return (-1);
417 		}
418 		wait_info(pid, status, &lwpinfo);
419 
420 		if (ptrace(PT_FOLLOW_FORK, pid, 0, 1) < 0) {
421 			perror("PT_FOLLOW_FORK");
422 			ptrace(PT_KILL, pid, NULL, 0);
423 			return (2);
424 		}
425 
426 		while ((pid1 = trace(pid)) >= 0) {
427 			if (pid1 != 0) {
428 				printf(TRACE "attached to pid %d\n", pid1);
429 #if 0
430 				kill(pid1, SIGCONT);
431 #endif
432 				if (waitpid(pid1, &status, 0) == -1) {
433 					perror("waitpid");
434 					return (-1);
435 				}
436 				printf(TRACE "nested loop, pid %d status %s\n",
437 				    pid1, decode_wait_status(status));
438 				assert(WIFSTOPPED(status));
439 				assert(WSTOPSIG(status) == SIGSTOP);
440 				if (ptrace(PT_LWPINFO, pid1, (caddr_t)&lwpinfo,
441 				    sizeof(lwpinfo)) < 0) {
442 					perror("PT_LWPINFO");
443 					ptrace(PT_KILL, pid1, NULL, 0);
444 					return (-1);
445 				}
446 				wait_info(pid1, status, &lwpinfo);
447 
448 				while (trace(pid1) >= 0)
449 					;
450 			}
451 		}
452 
453 		ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
454 	}
455 	return (0);
456 }
457