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