ptrace_test.c revision 1.2
1/* $OpenBSD: ptrace_test.c,v 1.2 2020/03/16 12:00:19 mpi Exp $ */ 2 3/*- 4 * Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include "macros.h" 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD$"); 32 33#include <sys/types.h> 34#include <sys/event.h> 35#include <sys/file.h> 36#include <sys/time.h> 37#include <sys/proc.h> 38#include <sys/ptrace.h> 39#include <sys/queue.h> 40#include <sys/syscall.h> 41#include <sys/sysctl.h> 42#include <sys/user.h> 43#include <sys/wait.h> 44#include <errno.h> 45#include <machine/cpufunc.h> 46#include <pthread.h> 47#include <sched.h> 48#include <semaphore.h> 49#include <signal.h> 50#include <stdio.h> 51#include <stdlib.h> 52#include <unistd.h> 53#include "atf-c.h" 54 55/* 56 * A variant of ATF_REQUIRE that is suitable for use in child 57 * processes. This only works if the parent process is tripped up by 58 * the early exit and fails some requirement itself. 59 */ 60#define CHILD_REQUIRE(exp) do { \ 61 if (!(exp)) \ 62 child_fail_require(__FILE__, __LINE__, \ 63 #exp " not met"); \ 64 } while (0) 65 66static __dead2 void 67child_fail_require(const char *file, int line, const char *str) 68{ 69 char buf[128]; 70 71 snprintf(buf, sizeof(buf), "%s:%d: %s\n", file, line, str); 72 write(2, buf, strlen(buf)); 73 _exit(32); 74} 75 76static void 77trace_me(void) 78{ 79 80 /* Attach the parent process as a tracer of this process. */ 81 CHILD_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1); 82 83 /* Trigger a stop. */ 84 raise(SIGSTOP); 85} 86 87static void 88attach_child(pid_t pid) 89{ 90 pid_t wpid; 91 int status; 92 93 ATF_REQUIRE(ptrace(PT_ATTACH, pid, NULL, 0) == 0); 94 95 wpid = waitpid(pid, &status, 0); 96 ATF_REQUIRE(wpid == pid); 97 ATF_REQUIRE(WIFSTOPPED(status)); 98 ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); 99} 100 101static void 102wait_for_zombie(pid_t pid) 103{ 104 105 /* 106 * Wait for a process to exit. This is kind of gross, but 107 * there is not a better way. 108 * 109 * Prior to r325719, the kern.proc.pid.<pid> sysctl failed 110 * with ESRCH. After that change, a valid struct kinfo_proc 111 * is returned for zombies with ki_stat set to SZOMB. 112 */ 113 for (;;) { 114 struct kinfo_proc kp; 115 size_t len; 116 int mib[6]; 117 118 mib[0] = CTL_KERN; 119 mib[1] = KERN_PROC; 120 mib[2] = KERN_PROC_PID; 121 mib[3] = pid; 122 mib[4] = len = sizeof(kp); 123 mib[5] = 1; 124 if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) { 125 ATF_REQUIRE(errno == ESRCH); 126 break; 127 } 128 if (kp.p_stat == SDEAD) 129 break; 130 usleep(5000); 131 } 132} 133 134/* 135 * Verify that a parent debugger process "sees" the exit of a debugged 136 * process exactly once when attached via PT_TRACE_ME. 137 */ 138ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_trace_me); 139ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc) 140{ 141 pid_t child, wpid; 142 int status; 143 144 ATF_REQUIRE((child = fork()) != -1); 145 if (child == 0) { 146 /* Child process. */ 147 trace_me(); 148 149 _exit(1); 150 } 151 152 /* Parent process. */ 153 154 /* The first wait() should report the stop from SIGSTOP. */ 155 wpid = waitpid(child, &status, 0); 156 printf("first %d\n", wpid); 157 ATF_REQUIRE(wpid == child); 158 ATF_REQUIRE(WIFSTOPPED(status)); 159 ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP); 160 161 /* Continue the child ignoring the SIGSTOP. */ 162 ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); 163 164 /* The second wait() should report the exit status. */ 165 wpid = waitpid(child, &status, 0); 166 printf("second %d\n", wpid); 167 ATF_REQUIRE(wpid == child); 168 ATF_REQUIRE(WIFEXITED(status)); 169 ATF_REQUIRE(WEXITSTATUS(status) == 1); 170 171 /* The child should no longer exist. */ 172 wpid = waitpid(child, &status, 0); 173 printf("third %d\n", wpid); 174 ATF_REQUIRE(wpid == -1); 175 ATF_REQUIRE(errno == ECHILD); 176} 177 178/* 179 * Verify that a parent debugger process "sees" the exit of a debugged 180 * process exactly once when attached via PT_ATTACH. 181 */ 182ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_attach); 183ATF_TC_BODY(ptrace__parent_wait_after_attach, tc) 184{ 185 pid_t child, wpid; 186 int cpipe[2], status; 187 char c; 188 189 ATF_REQUIRE(pipe(cpipe) == 0); 190 ATF_REQUIRE((child = fork()) != -1); 191 if (child == 0) { 192 /* Child process. */ 193 close(cpipe[0]); 194 195 /* Wait for the parent to attach. */ 196 CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == 0); 197 198 _exit(1); 199 } 200 close(cpipe[1]); 201 202 /* Parent process. */ 203 204 /* Attach to the child process. */ 205 attach_child(child); 206 207 /* Continue the child ignoring the SIGSTOP. */ 208 ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); 209 210 /* Signal the child to exit. */ 211 close(cpipe[0]); 212 213 /* The second wait() should report the exit status. */ 214 wpid = waitpid(child, &status, 0); 215 ATF_REQUIRE(wpid == child); 216 ATF_REQUIRE(WIFEXITED(status)); 217 ATF_REQUIRE(WEXITSTATUS(status) == 1); 218 219 /* The child should no longer exist. */ 220 wpid = waitpid(child, &status, 0); 221 ATF_REQUIRE(wpid == -1); 222 ATF_REQUIRE(errno == ECHILD); 223} 224 225/* 226 * Verify that a parent process "sees" the exit of a debugged process only 227 * after the debugger has seen it. 228 */ 229ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_child_debugger); 230ATF_TC_BODY(ptrace__parent_sees_exit_after_child_debugger, tc) 231{ 232 pid_t child, debugger, wpid; 233 int cpipe[2], dpipe[2], status; 234 char c; 235 236 if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false)) 237 atf_tc_skip("https://bugs.freebsd.org/239399"); 238 239 ATF_REQUIRE(pipe(cpipe) == 0); 240 ATF_REQUIRE((child = fork()) != -1); 241 242 if (child == 0) { 243 /* Child process. */ 244 close(cpipe[0]); 245 246 /* Wait for parent to be ready. */ 247 CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c)); 248 249 _exit(1); 250 } 251 close(cpipe[1]); 252 253 ATF_REQUIRE(pipe(dpipe) == 0); 254 ATF_REQUIRE((debugger = fork()) != -1); 255 256 if (debugger == 0) { 257 /* Debugger process. */ 258 close(dpipe[0]); 259 260 CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); 261 262 wpid = waitpid(child, &status, 0); 263 CHILD_REQUIRE(wpid == child); 264 CHILD_REQUIRE(WIFSTOPPED(status)); 265 CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); 266 267 CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); 268 269 /* Signal parent that debugger is attached. */ 270 CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c)); 271 272 /* Wait for parent's failed wait. */ 273 CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == 0); 274 275 wpid = waitpid(child, &status, 0); 276 CHILD_REQUIRE(wpid == child); 277 CHILD_REQUIRE(WIFEXITED(status)); 278 CHILD_REQUIRE(WEXITSTATUS(status) == 1); 279 280 _exit(0); 281 } 282 close(dpipe[1]); 283 284 /* Parent process. */ 285 286 /* Wait for the debugger to attach to the child. */ 287 ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c)); 288 289 /* Release the child. */ 290 ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c)); 291 ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0); 292 close(cpipe[0]); 293 294 wait_for_zombie(child); 295 296 /* 297 * This wait should return a pid of 0 to indicate no status to 298 * report. The parent should see the child as non-exited 299 * until the debugger sees the exit. 300 */ 301 wpid = waitpid(child, &status, WNOHANG); 302 ATF_REQUIRE(wpid == 0); 303 304 /* Signal the debugger to wait for the child. */ 305 close(dpipe[0]); 306 307 /* Wait for the debugger. */ 308 wpid = waitpid(debugger, &status, 0); 309 ATF_REQUIRE(wpid == debugger); 310 ATF_REQUIRE(WIFEXITED(status)); 311 ATF_REQUIRE(WEXITSTATUS(status) == 0); 312 313 /* The child process should now be ready. */ 314 wpid = waitpid(child, &status, WNOHANG); 315 ATF_REQUIRE(wpid == child); 316 ATF_REQUIRE(WIFEXITED(status)); 317 ATF_REQUIRE(WEXITSTATUS(status) == 1); 318} 319 320/* 321 * Verify that a parent process "sees" the exit of a debugged process 322 * only after a non-direct-child debugger has seen it. In particular, 323 * various wait() calls in the parent must avoid failing with ESRCH by 324 * checking the parent's orphan list for the debugee. 325 */ 326ATF_TC_WITHOUT_HEAD(ptrace__parent_sees_exit_after_unrelated_debugger); 327ATF_TC_BODY(ptrace__parent_sees_exit_after_unrelated_debugger, tc) 328{ 329 pid_t child, debugger, fpid, wpid; 330 int cpipe[2], dpipe[2], status; 331 char c; 332 333 ATF_REQUIRE(pipe(cpipe) == 0); 334 ATF_REQUIRE((child = fork()) != -1); 335 336 if (child == 0) { 337 /* Child process. */ 338 close(cpipe[0]); 339 340 /* Wait for parent to be ready. */ 341 CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == sizeof(c)); 342 343 _exit(1); 344 } 345 close(cpipe[1]); 346 347 ATF_REQUIRE(pipe(dpipe) == 0); 348 ATF_REQUIRE((debugger = fork()) != -1); 349 350 if (debugger == 0) { 351 /* Debugger parent. */ 352 353 /* 354 * Fork again and drop the debugger parent so that the 355 * debugger is not a child of the main parent. 356 */ 357 CHILD_REQUIRE((fpid = fork()) != -1); 358 if (fpid != 0) 359 _exit(2); 360 361 /* Debugger process. */ 362 close(dpipe[0]); 363 364 CHILD_REQUIRE(ptrace(PT_ATTACH, child, NULL, 0) != -1); 365 366 wpid = waitpid(child, &status, 0); 367 CHILD_REQUIRE(wpid == child); 368 CHILD_REQUIRE(WIFSTOPPED(status)); 369 CHILD_REQUIRE(WSTOPSIG(status) == SIGSTOP); 370 371 CHILD_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1); 372 373 /* Signal parent that debugger is attached. */ 374 CHILD_REQUIRE(write(dpipe[1], &c, sizeof(c)) == sizeof(c)); 375 376 /* Wait for parent's failed wait. */ 377 CHILD_REQUIRE(read(dpipe[1], &c, sizeof(c)) == sizeof(c)); 378 379 wpid = waitpid(child, &status, 0); 380 CHILD_REQUIRE(wpid == child); 381 CHILD_REQUIRE(WIFEXITED(status)); 382 CHILD_REQUIRE(WEXITSTATUS(status) == 1); 383 384 _exit(0); 385 } 386 close(dpipe[1]); 387 388 /* Parent process. */ 389 390 /* Wait for the debugger parent process to exit. */ 391 wpid = waitpid(debugger, &status, 0); 392 ATF_REQUIRE(wpid == debugger); 393 ATF_REQUIRE(WIFEXITED(status)); 394 ATF_REQUIRE(WEXITSTATUS(status) == 2); 395 396 /* A WNOHANG wait here should see the non-exited child. */ 397 wpid = waitpid(child, &status, WNOHANG); 398 ATF_REQUIRE(wpid == 0); 399 400 /* Wait for the debugger to attach to the child. */ 401 ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == sizeof(c)); 402 403 /* Release the child. */ 404 ATF_REQUIRE(write(cpipe[0], &c, sizeof(c)) == sizeof(c)); 405 ATF_REQUIRE(read(cpipe[0], &c, sizeof(c)) == 0); 406 close(cpipe[0]); 407 408 wait_for_zombie(child); 409 410 /* 411 * This wait should return a pid of 0 to indicate no status to 412 * report. The parent should see the child as non-exited 413 * until the debugger sees the exit. 414 */ 415 wpid = waitpid(child, &status, WNOHANG); 416 ATF_REQUIRE(wpid == 0); 417 418 /* Signal the debugger to wait for the child. */ 419 ATF_REQUIRE(write(dpipe[0], &c, sizeof(c)) == sizeof(c)); 420 421 /* Wait for the debugger. */ 422 ATF_REQUIRE(read(dpipe[0], &c, sizeof(c)) == 0); 423 close(dpipe[0]); 424 425 /* The child process should now be ready. */ 426 wpid = waitpid(child, &status, WNOHANG); 427 ATF_REQUIRE(wpid == child); 428 ATF_REQUIRE(WIFEXITED(status)); 429 ATF_REQUIRE(WEXITSTATUS(status) == 1); 430} 431 432ATF_TP_ADD_TCS(tp) 433{ 434 435 ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me); 436 ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach); 437 ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_child_debugger); 438 ATF_TP_ADD_TC(tp, ptrace__parent_sees_exit_after_unrelated_debugger); 439 440 return (atf_no_error()); 441} 442 443