t_ptrace_topology_wait.h revision 1.2 1 /* $NetBSD: t_ptrace_topology_wait.h,v 1.2 2025/05/02 02:24:32 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2016, 2017, 2018, 2019, 2020 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 ATF_TC(traceme_pid1_parent);
30 ATF_TC_HEAD(traceme_pid1_parent, tc)
31 {
32 atf_tc_set_md_var(tc, "descr",
33 "Verify that PT_TRACE_ME is not allowed when our parent is PID1");
34 }
35
36 ATF_TC_BODY(traceme_pid1_parent, tc)
37 {
38 struct msg_fds parent_child;
39 int exitval_child1 = 1, exitval_child2 = 2;
40 pid_t child1, child2, wpid;
41 uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
42 #if defined(TWAIT_HAVE_STATUS)
43 int status;
44 #endif
45
46 SYSCALL_REQUIRE(msg_open(&parent_child) == 0);
47
48 DPRINTF("Before forking process PID=%d\n", getpid());
49 SYSCALL_REQUIRE((child1 = fork()) != -1);
50 if (child1 == 0) {
51 DPRINTF("Before forking process PID=%d\n", getpid());
52 SYSCALL_REQUIRE((child2 = fork()) != -1);
53 if (child2 != 0) {
54 DPRINTF("Parent process PID=%d, child2's PID=%d\n",
55 getpid(), child2);
56 _exit(exitval_child1);
57 }
58 CHILD_FROM_PARENT("exit child1", parent_child, msg);
59
60 DPRINTF("Assert that our parent is PID1 (initproc)\n");
61 FORKEE_ASSERT_EQ(getppid(), 1);
62
63 DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
64 FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) == -1);
65 SYSCALL_REQUIRE_ERRNO(errno, EPERM);
66
67 CHILD_TO_PARENT("child2 exiting", parent_child, msg);
68
69 _exit(exitval_child2);
70 }
71 DPRINTF("Parent process PID=%d, child1's PID=%d\n", getpid(), child1);
72
73 DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
74 TWAIT_REQUIRE_SUCCESS(
75 wpid = TWAIT_GENERIC(child1, &status, WEXITED), child1);
76
77 validate_status_exited(status, exitval_child1);
78
79 DPRINTF("Notify that child1 is dead\n");
80 PARENT_TO_CHILD("exit child1", parent_child, msg);
81
82 DPRINTF("Wait for exiting of child2\n");
83 PARENT_FROM_CHILD("child2 exiting", parent_child, msg);
84 }
85
86 /// ----------------------------------------------------------------------------
87
88 #if defined(TWAIT_HAVE_PID)
89 static void
90 tracer_sees_terminaton_before_the_parent_raw(bool notimeout, bool unrelated,
91 bool stopped)
92 {
93 /*
94 * notimeout - disable timeout in await zombie function
95 * unrelated - attach from unrelated tracer reparented to initproc
96 * stopped - attach to a stopped process
97 */
98
99 struct msg_fds parent_tracee, parent_tracer;
100 const int exitval_tracee = 5;
101 const int exitval_tracer = 10;
102 pid_t tracee, tracer, wpid;
103 uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
104 #if defined(TWAIT_HAVE_STATUS)
105 int status;
106 #endif
107
108 /*
109 * Only a subset of options are supported.
110 */
111 ATF_REQUIRE((!notimeout && !unrelated && !stopped) ||
112 (!notimeout && unrelated && !stopped) ||
113 (notimeout && !unrelated && !stopped) ||
114 (!notimeout && unrelated && stopped));
115
116 DPRINTF("Spawn tracee\n");
117 SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
118 tracee = atf_utils_fork();
119 if (tracee == 0) {
120 if (stopped) {
121 DPRINTF("Stop self PID %d\n", getpid());
122 raise(SIGSTOP);
123 }
124
125 // Wait for parent to let us exit
126 CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
127 _exit(exitval_tracee);
128 }
129
130 DPRINTF("Spawn debugger\n");
131 SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0);
132 tracer = atf_utils_fork();
133 if (tracer == 0) {
134 if(unrelated) {
135 /* Fork again and drop parent to reattach to PID 1 */
136 tracer = atf_utils_fork();
137 if (tracer != 0)
138 _exit(exitval_tracer);
139 }
140
141 if (stopped) {
142 DPRINTF("Await for a stopped parent PID %d\n", tracee);
143 await_stopped(tracee);
144 }
145
146 DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid());
147 FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
148
149 /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
150 FORKEE_REQUIRE_SUCCESS(
151 wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
152
153 forkee_status_stopped(status, SIGSTOP);
154
155 /* Resume tracee with PT_CONTINUE */
156 FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
157
158 /* Inform parent that tracer has attached to tracee */
159 CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
160
161 /* Wait for parent to tell use that tracee should have exited */
162 CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
163
164 /* Wait for tracee and assert that it exited */
165 FORKEE_REQUIRE_SUCCESS(
166 wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
167
168 forkee_status_exited(status, exitval_tracee);
169 DPRINTF("Tracee %d exited with %d\n", tracee, exitval_tracee);
170
171 DPRINTF("Before exiting of the tracer process\n");
172 _exit(unrelated ? 0 /* collect by initproc */ : exitval_tracer);
173 }
174
175 if (unrelated) {
176 DPRINTF("Wait for the tracer process (direct child) to exit "
177 "calling %s()\n", TWAIT_FNAME);
178 TWAIT_REQUIRE_SUCCESS(
179 wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
180
181 validate_status_exited(status, exitval_tracer);
182
183 DPRINTF("Wait for the non-exited tracee process with %s()\n",
184 TWAIT_FNAME);
185 TWAIT_REQUIRE_SUCCESS(
186 wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0);
187 }
188
189 DPRINTF("Wait for the tracer to attach to the tracee\n");
190 PARENT_FROM_CHILD("tracer ready", parent_tracer, msg);
191
192 DPRINTF("Resume the tracee and let it exit\n");
193 PARENT_TO_CHILD("exit tracee", parent_tracee, msg);
194
195 DPRINTF("Detect that tracee is zombie\n");
196 if (notimeout)
197 await_zombie_raw(tracee, 0);
198 else
199 await_zombie(tracee);
200
201 DPRINTF("Assert that there is no status about tracee %d - "
202 "Tracer must detect zombie first - calling %s()\n", tracee,
203 TWAIT_FNAME);
204 TWAIT_REQUIRE_SUCCESS(
205 wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
206
207 if (unrelated) {
208 DPRINTF("Resume the tracer and let it detect exited tracee\n");
209 PARENT_TO_CHILD("Message 2", parent_tracer, msg);
210 } else {
211 DPRINTF("Tell the tracer child should have exited\n");
212 PARENT_TO_CHILD("wait for tracee exit", parent_tracer, msg);
213 DPRINTF("Wait for tracer to finish its job and exit - calling "
214 "%s()\n", TWAIT_FNAME);
215
216 DPRINTF("Wait from tracer child to complete waiting for "
217 "tracee\n");
218 TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
219 tracer);
220
221 validate_status_exited(status, exitval_tracer);
222 }
223
224 DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n",
225 TWAIT_FNAME);
226 TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
227
228 validate_status_exited(status, exitval_tracee);
229
230 msg_close(&parent_tracer);
231 msg_close(&parent_tracee);
232 }
233
234 ATF_TC(tracer_sees_terminaton_before_the_parent);
235 ATF_TC_HEAD(tracer_sees_terminaton_before_the_parent, tc)
236 {
237 atf_tc_set_md_var(tc, "descr",
238 "Assert that tracer sees process termination before the parent");
239 }
240
241 ATF_TC_BODY(tracer_sees_terminaton_before_the_parent, tc)
242 {
243
244 tracer_sees_terminaton_before_the_parent_raw(false, false, false);
245 }
246
247 ATF_TC(tracer_sysctl_lookup_without_duplicates);
248 ATF_TC_HEAD(tracer_sysctl_lookup_without_duplicates, tc)
249 {
250 atf_tc_set_md_var(tc, "timeout", "15");
251 atf_tc_set_md_var(tc, "descr",
252 "Assert that await_zombie() in attach1 always finds a single "
253 "process and no other error is reported");
254 }
255
256 ATF_TC_BODY(tracer_sysctl_lookup_without_duplicates, tc)
257 {
258 time_t start, end;
259 double diff;
260 unsigned long N = 0;
261
262 /*
263 * Reuse this test with tracer_sees_terminaton_before_the_parent_raw().
264 * This test body isn't specific to this race, however it's just good
265 * enough for this purposes, no need to invent a dedicated code flow.
266 */
267
268 start = time(NULL);
269 while (true) {
270 DPRINTF("Step: %lu\n", N);
271 tracer_sees_terminaton_before_the_parent_raw(true, false,
272 false);
273 end = time(NULL);
274 diff = difftime(end, start);
275 if (diff >= 5.0)
276 break;
277 ++N;
278 }
279 DPRINTF("Iterations: %lu\n", N);
280 }
281
282 ATF_TC(unrelated_tracer_sees_terminaton_before_the_parent);
283 ATF_TC_HEAD(unrelated_tracer_sees_terminaton_before_the_parent, tc)
284 {
285 atf_tc_set_md_var(tc, "descr",
286 "Assert that tracer sees process termination before the parent");
287 }
288
289 ATF_TC_BODY(unrelated_tracer_sees_terminaton_before_the_parent, tc)
290 {
291
292 tracer_sees_terminaton_before_the_parent_raw(false, true, false);
293 }
294
295 ATF_TC(tracer_attach_to_unrelated_stopped_process);
296 ATF_TC_HEAD(tracer_attach_to_unrelated_stopped_process, tc)
297 {
298 atf_tc_set_md_var(tc, "descr",
299 "Assert that tracer can attach to an unrelated stopped process");
300 }
301
302 ATF_TC_BODY(tracer_attach_to_unrelated_stopped_process, tc)
303 {
304
305 tracer_sees_terminaton_before_the_parent_raw(false, true, true);
306 }
307 #endif
308
309 /// ----------------------------------------------------------------------------
310
311 static void
312 parent_attach_to_its_child(bool stopped)
313 {
314 struct msg_fds parent_tracee;
315 const int exitval_tracee = 5;
316 pid_t tracee, wpid;
317 uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
318 #if defined(TWAIT_HAVE_STATUS)
319 int status;
320 #endif
321
322 DPRINTF("Spawn tracee\n");
323 SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
324 tracee = atf_utils_fork();
325 if (tracee == 0) {
326 CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
327 DPRINTF("Parent should now attach to tracee\n");
328
329 if (stopped) {
330 DPRINTF("Stop self PID %d\n", getpid());
331 SYSCALL_REQUIRE(raise(SIGSTOP) != -1);
332 }
333
334 CHILD_FROM_PARENT("Message 2", parent_tracee, msg);
335 /* Wait for message from the parent */
336 _exit(exitval_tracee);
337 }
338 PARENT_TO_CHILD("Message 1", parent_tracee, msg);
339
340 if (stopped) {
341 DPRINTF("Await for a stopped tracee PID %d\n", tracee);
342 await_stopped(tracee);
343 }
344
345 DPRINTF("Before calling PT_ATTACH for tracee %d\n", tracee);
346 SYSCALL_REQUIRE(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
347
348 DPRINTF("Wait for the stopped tracee process with %s()\n",
349 TWAIT_FNAME);
350 TWAIT_REQUIRE_SUCCESS(
351 wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
352
353 validate_status_stopped(status, SIGSTOP);
354
355 DPRINTF("Resume tracee with PT_CONTINUE\n");
356 SYSCALL_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
357
358 DPRINTF("Let the tracee exit now\n");
359 PARENT_TO_CHILD("Message 2", parent_tracee, msg);
360
361 DPRINTF("Wait for tracee to exit with %s()\n", TWAIT_FNAME);
362 TWAIT_REQUIRE_SUCCESS(
363 wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
364
365 validate_status_exited(status, exitval_tracee);
366
367 DPRINTF("Before calling %s() for tracee\n", TWAIT_FNAME);
368 TWAIT_REQUIRE_FAILURE(ECHILD,
369 wpid = TWAIT_GENERIC(tracee, &status, 0));
370
371 msg_close(&parent_tracee);
372 }
373
374 ATF_TC(parent_attach_to_its_child);
375 ATF_TC_HEAD(parent_attach_to_its_child, tc)
376 {
377 atf_tc_set_md_var(tc, "descr",
378 "Assert that tracer parent can PT_ATTACH to its child");
379 }
380
381 ATF_TC_BODY(parent_attach_to_its_child, tc)
382 {
383
384 parent_attach_to_its_child(false);
385 }
386
387 ATF_TC(parent_attach_to_its_stopped_child);
388 ATF_TC_HEAD(parent_attach_to_its_stopped_child, tc)
389 {
390 atf_tc_set_md_var(tc, "descr",
391 "Assert that tracer parent can PT_ATTACH to its stopped child");
392 }
393
394 ATF_TC_BODY(parent_attach_to_its_stopped_child, tc)
395 {
396
397 parent_attach_to_its_child(true);
398 }
399
400 /// ----------------------------------------------------------------------------
401
402 static void
403 child_attach_to_its_parent(bool stopped)
404 {
405 struct msg_fds parent_tracee;
406 const int exitval_tracer = 5;
407 pid_t tracer, wpid;
408 uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
409 #if defined(TWAIT_HAVE_STATUS)
410 int status;
411 #endif
412
413 DPRINTF("Spawn tracer\n");
414 SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
415 tracer = atf_utils_fork();
416 if (tracer == 0) {
417 /* Wait for message from the parent */
418 CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
419
420 if (stopped) {
421 DPRINTF("Await for a stopped parent PID %d\n",
422 getppid());
423 await_stopped(getppid());
424 }
425
426 DPRINTF("Attach to parent PID %d with PT_ATTACH from child\n",
427 getppid());
428 FORKEE_ASSERT(ptrace(PT_ATTACH, getppid(), NULL, 0) != -1);
429
430 DPRINTF("Wait for the stopped parent process with %s()\n",
431 TWAIT_FNAME);
432 FORKEE_REQUIRE_SUCCESS(
433 wpid = TWAIT_GENERIC(getppid(), &status, 0), getppid());
434
435 forkee_status_stopped(status, SIGSTOP);
436
437 DPRINTF("Resume parent with PT_DETACH\n");
438 FORKEE_ASSERT(ptrace(PT_DETACH, getppid(), (void *)1, 0)
439 != -1);
440
441 /* Tell parent we are ready */
442 CHILD_TO_PARENT("Message 1", parent_tracee, msg);
443
444 _exit(exitval_tracer);
445 }
446
447 DPRINTF("Wait for the tracer to become ready\n");
448 PARENT_TO_CHILD("Message 1", parent_tracee, msg);
449
450 if (stopped) {
451 DPRINTF("Stop self PID %d\n", getpid());
452 SYSCALL_REQUIRE(raise(SIGSTOP) != -1);
453 }
454
455 DPRINTF("Allow the tracer to exit now\n");
456 PARENT_FROM_CHILD("Message 1", parent_tracee, msg);
457
458 DPRINTF("Wait for tracer to exit with %s()\n", TWAIT_FNAME);
459 TWAIT_REQUIRE_SUCCESS(
460 wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
461
462 validate_status_exited(status, exitval_tracer);
463
464 DPRINTF("Before calling %s() for tracer\n", TWAIT_FNAME);
465 TWAIT_REQUIRE_FAILURE(ECHILD,
466 wpid = TWAIT_GENERIC(tracer, &status, 0));
467
468 msg_close(&parent_tracee);
469 }
470
471 ATF_TC(child_attach_to_its_parent);
472 ATF_TC_HEAD(child_attach_to_its_parent, tc)
473 {
474 atf_tc_set_md_var(tc, "descr",
475 "Assert that tracer child can PT_ATTACH to its parent");
476 }
477
478 ATF_TC_BODY(child_attach_to_its_parent, tc)
479 {
480
481 child_attach_to_its_parent(false);
482 }
483
484 ATF_TC(child_attach_to_its_stopped_parent);
485 ATF_TC_HEAD(child_attach_to_its_stopped_parent, tc)
486 {
487 atf_tc_set_md_var(tc, "descr",
488 "Assert that tracer child can PT_ATTACH to its stopped parent");
489 }
490
491 ATF_TC_BODY(child_attach_to_its_stopped_parent, tc)
492 {
493 /*
494 * The ATF framework (atf-run) does not tolerate raise(SIGSTOP), as
495 * this causes a pipe (established from atf-run) to be broken.
496 * atf-run uses this mechanism to monitor whether a test is alive.
497 *
498 * As a workaround spawn this test as a subprocess.
499 */
500
501 const int exitval = 15;
502 pid_t child, wpid;
503 #if defined(TWAIT_HAVE_STATUS)
504 int status;
505 #endif
506
507 SYSCALL_REQUIRE((child = fork()) != -1);
508 if (child == 0) {
509 child_attach_to_its_parent(true);
510 _exit(exitval);
511 } else {
512 DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
513 TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
514
515 validate_status_exited(status, exitval);
516
517 DPRINTF("Before calling %s() for the exited child\n", TWAIT_FNAME);
518 TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
519 }
520 }
521
522 /// ----------------------------------------------------------------------------
523
524 #if defined(TWAIT_HAVE_PID)
525
526 enum tracee_sees_its_original_parent_type {
527 TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID,
528 TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2,
529 TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS
530 };
531
532 static void
533 tracee_sees_its_original_parent(enum tracee_sees_its_original_parent_type type)
534 {
535 struct msg_fds parent_tracer, parent_tracee;
536 const int exitval_tracee = 5;
537 const int exitval_tracer = 10;
538 pid_t parent, tracee, tracer, wpid;
539 uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
540 #if defined(TWAIT_HAVE_STATUS)
541 int status;
542 #endif
543 /* sysctl(3) - kinfo_proc2 */
544 int name[CTL_MAXNAME];
545 struct kinfo_proc2 kp;
546 size_t len = sizeof(kp);
547 unsigned int namelen;
548
549 /* procfs - status */
550 FILE *fp;
551 struct stat st;
552 const char *fname = "/proc/curproc/status";
553 char s_executable[MAXPATHLEN];
554 int s_pid, s_ppid;
555 int rv;
556
557 if (type == TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS) {
558 SYSCALL_REQUIRE(
559 (rv = stat(fname, &st)) == 0 || (errno == ENOENT));
560 if (rv != 0)
561 atf_tc_skip("/proc/curproc/status not found");
562 }
563
564 DPRINTF("Spawn tracee\n");
565 SYSCALL_REQUIRE(msg_open(&parent_tracer) == 0);
566 SYSCALL_REQUIRE(msg_open(&parent_tracee) == 0);
567 tracee = atf_utils_fork();
568 if (tracee == 0) {
569 parent = getppid();
570
571 /* Emit message to the parent */
572 CHILD_TO_PARENT("tracee ready", parent_tracee, msg);
573 CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
574
575 switch (type) {
576 case TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID:
577 FORKEE_ASSERT_EQ(parent, getppid());
578 break;
579 case TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2:
580 namelen = 0;
581 name[namelen++] = CTL_KERN;
582 name[namelen++] = KERN_PROC2;
583 name[namelen++] = KERN_PROC_PID;
584 name[namelen++] = getpid();
585 name[namelen++] = len;
586 name[namelen++] = 1;
587
588 FORKEE_ASSERT_EQ(
589 sysctl(name, namelen, &kp, &len, NULL, 0), 0);
590 FORKEE_ASSERT_EQ(parent, kp.p_ppid);
591 break;
592 case TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS:
593 /*
594 * Format:
595 * EXECUTABLE PID PPID ...
596 */
597 FORKEE_ASSERT((fp = fopen(fname, "r")) != NULL);
598 fscanf(fp, "%s %d %d", s_executable, &s_pid, &s_ppid);
599 FORKEE_ASSERT_EQ(fclose(fp), 0);
600 FORKEE_ASSERT_EQ(parent, s_ppid);
601 break;
602 }
603
604 _exit(exitval_tracee);
605 }
606 DPRINTF("Wait for child to record its parent identifier (pid)\n");
607 PARENT_FROM_CHILD("tracee ready", parent_tracee, msg);
608
609 DPRINTF("Spawn debugger\n");
610 tracer = atf_utils_fork();
611 if (tracer == 0) {
612 /* No IPC to communicate with the child */
613 DPRINTF("Before calling PT_ATTACH from tracee %d\n", getpid());
614 FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
615
616 /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
617 FORKEE_REQUIRE_SUCCESS(
618 wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
619
620 forkee_status_stopped(status, SIGSTOP);
621
622 /* Resume tracee with PT_CONTINUE */
623 FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
624
625 /* Inform parent that tracer has attached to tracee */
626 CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
627
628 /* Wait for parent to tell use that tracee should have exited */
629 CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
630
631 /* Wait for tracee and assert that it exited */
632 FORKEE_REQUIRE_SUCCESS(
633 wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
634
635 forkee_status_exited(status, exitval_tracee);
636
637 DPRINTF("Before exiting of the tracer process\n");
638 _exit(exitval_tracer);
639 }
640
641 DPRINTF("Wait for the tracer to attach to the tracee\n");
642 PARENT_FROM_CHILD("tracer ready", parent_tracer, msg);
643
644 DPRINTF("Resume the tracee and let it exit\n");
645 PARENT_TO_CHILD("exit tracee", parent_tracee, msg);
646
647 DPRINTF("Detect that tracee is zombie\n");
648 await_zombie(tracee);
649
650 DPRINTF("Assert that there is no status about tracee - "
651 "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
652 TWAIT_REQUIRE_SUCCESS(
653 wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
654
655 DPRINTF("Tell the tracer child should have exited\n");
656 PARENT_TO_CHILD("wait for tracee exit", parent_tracer, msg);
657
658 DPRINTF("Wait from tracer child to complete waiting for tracee\n");
659 TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
660 tracer);
661
662 validate_status_exited(status, exitval_tracer);
663
664 DPRINTF("Wait for tracee to finish its job and exit - calling %s()\n",
665 TWAIT_FNAME);
666 TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
667 tracee);
668
669 validate_status_exited(status, exitval_tracee);
670
671 msg_close(&parent_tracer);
672 msg_close(&parent_tracee);
673 }
674
675 #define TRACEE_SEES_ITS_ORIGINAL_PARENT(test, type, descr) \
676 ATF_TC(test); \
677 ATF_TC_HEAD(test, tc) \
678 { \
679 atf_tc_set_md_var(tc, "descr", \
680 "Assert that tracee sees its original parent when being traced " \
681 "(check " descr ")"); \
682 } \
683 \
684 ATF_TC_BODY(test, tc) \
685 { \
686 \
687 tracee_sees_its_original_parent(type); \
688 }
689
690 TRACEE_SEES_ITS_ORIGINAL_PARENT(
691 tracee_sees_its_original_parent_getppid,
692 TRACEE_SEES_ITS_ORIGINAL_PARENT_GETPPID,
693 "getppid(2)");
694 TRACEE_SEES_ITS_ORIGINAL_PARENT(
695 tracee_sees_its_original_parent_sysctl_kinfo_proc2,
696 TRACEE_SEES_ITS_ORIGINAL_PARENT_SYSCTL_KINFO_PROC2,
697 "sysctl(3) and kinfo_proc2");
698 TRACEE_SEES_ITS_ORIGINAL_PARENT(
699 tracee_sees_its_original_parent_procfs_status,
700 TRACEE_SEES_ITS_ORIGINAL_PARENT_PROCFS_STATUS,
701 "the status file in procfs");
702 #endif
703
704 #define ATF_TP_ADD_TCS_PTRACE_WAIT_TOPOLOGY() \
705 ATF_TP_ADD_TC(tp, traceme_pid1_parent); \
706 ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sees_terminaton_before_the_parent); \
707 ATF_TP_ADD_TC_HAVE_PID(tp, tracer_sysctl_lookup_without_duplicates); \
708 ATF_TP_ADD_TC_HAVE_PID(tp, \
709 unrelated_tracer_sees_terminaton_before_the_parent); \
710 ATF_TP_ADD_TC_HAVE_PID(tp, tracer_attach_to_unrelated_stopped_process); \
711 ATF_TP_ADD_TC(tp, parent_attach_to_its_child); \
712 ATF_TP_ADD_TC(tp, parent_attach_to_its_stopped_child); \
713 ATF_TP_ADD_TC(tp, child_attach_to_its_parent); \
714 ATF_TP_ADD_TC(tp, child_attach_to_its_stopped_parent); \
715 ATF_TP_ADD_TC_HAVE_PID(tp, \
716 tracee_sees_its_original_parent_getppid); \
717 ATF_TP_ADD_TC_HAVE_PID(tp, \
718 tracee_sees_its_original_parent_sysctl_kinfo_proc2); \
719 ATF_TP_ADD_TC_HAVE_PID(tp, \
720 tracee_sees_its_original_parent_procfs_status);
721