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