summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormpi <mpi@openbsd.org>2020-02-28 12:48:30 +0000
committermpi <mpi@openbsd.org>2020-02-28 12:48:30 +0000
commit5ade3599362fa549bf3efb0278751bb10403b759 (patch)
treefcca1be1976448083bb7e0f78c2c8fb3b9349611
parentAdapt biosboot(8) so it can read boot(8) from an ffs2 filesystem. (diff)
downloadwireguard-openbsd-5ade3599362fa549bf3efb0278751bb10403b759.tar.xz
wireguard-openbsd-5ade3599362fa549bf3efb0278751bb10403b759.zip
Import ptrace(2) regression tests from FreeBSD.
Only the first two tests are currently passing and enabled. The second one already exposes the recently fixed bug with double wait(2) report. More tests will be enabled as they get fixed.
-rw-r--r--regress/sys/kern/ptrace2/Makefile72
-rw-r--r--regress/sys/kern/ptrace2/atf-c.c113
-rw-r--r--regress/sys/kern/ptrace2/atf-c.h100
-rw-r--r--regress/sys/kern/ptrace2/macros.h68
-rw-r--r--regress/sys/kern/ptrace2/ptrace_test.c232
5 files changed, 585 insertions, 0 deletions
diff --git a/regress/sys/kern/ptrace2/Makefile b/regress/sys/kern/ptrace2/Makefile
new file mode 100644
index 00000000000..fad37b26cb6
--- /dev/null
+++ b/regress/sys/kern/ptrace2/Makefile
@@ -0,0 +1,72 @@
+# $OpenBSD: Makefile,v 1.1 2020/02/28 12:48:30 mpi Exp $
+
+# Copyright (c) 2019 Moritz Buhl <openbsd@moritzbuhl.de>
+# Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# Each test program in PROGS may define several numbered subtests.
+# In a first step compile all programs and extract their parameters.
+# For each PROG define new regression subtests based on the test number.
+
+.if defined(NUMBERS)
+REGRESS_TARGETS = ${NUMBERS:S/^/run-${PROG}-/}
+.else
+REGRESS_TARGETS = ${PROGS:S/^/run-/}
+.endif
+
+PROGS = ptrace_test
+
+. for p in ${PROGS}
+SRCS_$p = $p.c atf-c.c
+. endfor
+
+LDADD = -lpthread
+
+.for p in ${PROGS}
+run-$p: $p
+ @echo "\n======== $@ ========"
+ ntests="`./$p -n`" && \
+ echo "1..$$ntests" && \
+ tnumbers="`jot -ns' ' - 1 $$ntests`" && \
+ ${.MAKE} -C ${.CURDIR} PROG=$p NUMBERS="$$tnumbers" regress
+.endfor
+
+.if defined(NUMBERS)
+CUR_USER != id -g
+
+. for n in ${NUMBERS}
+DESCR_$n != eval `./${PROG} -i $n` && echo $$DESCR
+REQ_USER_$n != eval `./${PROG} -i $n` && echo $$REQ_USER
+
+. if ${REQ_USER_$n} == "root"
+REGRESS_ROOT_TARGETS += run-${PROG}-$n
+. endif
+
+run-${PROG}-$n:
+ @echo "$n ${DESCR_$n}"
+. if ${REQ_USER_$n} == "root"
+ ${SUDO} ./${PROG} -r $n
+. elif ${REQ_USER_$n} == "unprivileged" && ${CUR_USER} == 0
+ ${SUDO} su ${BUILDUSER} -c exec ./${PROG} -r $n
+. elif ${REQ_USER_$n} == "unprivileged" || ${REQ_USER_$n} == ""
+ ./${PROG} -r $n
+. else
+ # bad REQ_USER: ${REQ_USER_$n}
+ false
+. endif
+
+. endfor
+.endif
+
+.include <bsd.regress.mk>
diff --git a/regress/sys/kern/ptrace2/atf-c.c b/regress/sys/kern/ptrace2/atf-c.c
new file mode 100644
index 00000000000..bb0e6e1adfe
--- /dev/null
+++ b/regress/sys/kern/ptrace2/atf-c.c
@@ -0,0 +1,113 @@
+/* $OpenBSD: atf-c.c,v 1.1 2020/02/28 12:48:30 mpi Exp $ */
+
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include "atf-c.h"
+
+void usage(void);
+
+int cleanup;
+int count;
+int inspect;
+int run;
+int test;
+
+int
+main(int argc, char *argv[])
+{
+ int ch, test;
+ const char *errstr, *num;
+
+ while ((ch = getopt(argc, argv, "c:i:nr:")) != -1) {
+ switch(ch) {
+ case 'c':
+ cleanup = 1;
+ num = optarg;
+ break;
+ case 'i':
+ inspect = 1;
+ num = optarg;
+ break;
+ case 'n':
+ count = 1;
+ break;
+ case 'r':
+ run = 1;
+ num = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (cleanup + count + inspect + run > 1)
+ usage();
+
+ if (cleanup || inspect || run) {
+ test = strtonum(num, 1, INT_MAX, &errstr);
+ if (errstr != NULL)
+ errx(1, "test # is %s: %s", errstr, argv[1]);
+ }
+ if (count)
+ printf("%d\n", atf_test(0, 0));
+ else if (cleanup)
+ ATF_CLEANUP(test);
+ else if (run)
+ ATF_RUN(test);
+ else if (inspect)
+ ATF_INSPECT(test);
+ else
+ usage();
+
+ return 0;
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-n] [-c|i|r test#]\n", getprogname());
+ exit(1);
+}
+
+void
+atf_require(int exp, int expected_errno, const char *expstr, const char *src,
+ const int lineno, char *fmt, ...)
+{
+ va_list args;
+ if (!(exp)) {
+ fprintf(stderr, "\n%s:%d: ", src, lineno);
+ if (fmt != NULL) {
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ } else {
+ fprintf(stderr, "'%s' evaluated to false\n", expstr);
+ }
+ exit(1);
+ } else if (expected_errno >= 0 && errno != expected_errno) {
+ fprintf(stderr, "\n%s:%d: ", src, lineno);
+ fprintf(stderr, "expected errno %d but got %d instead\n",
+ expected_errno, errno);
+ exit(1);
+ }
+ return;
+}
+
+void
+atf_tc_fail(char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ verrx(1, fmt, args);
+}
diff --git a/regress/sys/kern/ptrace2/atf-c.h b/regress/sys/kern/ptrace2/atf-c.h
new file mode 100644
index 00000000000..df613361a99
--- /dev/null
+++ b/regress/sys/kern/ptrace2/atf-c.h
@@ -0,0 +1,100 @@
+/* $OpenBSD: atf-c.h,v 1.1 2020/02/28 12:48:30 mpi Exp $ */
+/*
+ * Copyright (c) 2019 Moritz Buhl <openbsd@moritzbuhl.de>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if !defined(ATF_C_H)
+#define ATF_C_H
+
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+
+int atf_test(int, int);
+void atf_require(int, int, const char *, const char *, const int, char *, ...);
+void atf_tc_fail(char *, ...)
+ __attribute__((__noreturn__, __format__ (printf, 1, 2)));
+
+#define ATF_INSPECT_TEST 1
+#define ATF_RUN_TEST 2
+#define ATF_CLEANUP_TEST 3
+
+#define ATF_TC_FUNCTIONS(fn) \
+void atf_head_##fn(void); \
+void atf_body_##fn(void); \
+void atf_cleanup_##fn(void);
+
+#define ATF_TC(fn) \
+ATF_TC_FUNCTIONS(fn) \
+void atf_cleanup_##fn(void) { return; }
+
+#define ATF_TC_WITH_CLEANUP(fn) \
+ATF_TC_FUNCTIONS(fn)
+
+#define ATF_TC_WITHOUT_HEAD(fn) ATF_TC_HEAD(fn, NULL)
+#define ATF_TC_HEAD(fn, tc) void atf_head_##fn(void)
+#define ATF_TC_BODY(fn, tc) void atf_body_##fn(void)
+#define ATF_TC_CLEANUP(fn, tc) void atf_cleanup_##fn(void)
+
+
+#define ATF_TP_ADD_TCS(tp) int atf_test(int tst, int what)
+#define ATF_TP_ADD_TC(tp, fn) tst--; \
+ if (tst == 0) { \
+ if (what == ATF_RUN_TEST) \
+ atf_body_##fn(); \
+ return 0; \
+ }
+
+#define atf_no_error() (-tst)
+
+#define ATF_INSPECT(i) atf_test(i, ATF_INSPECT_TEST)
+#define ATF_RUN(i) atf_test(i, ATF_RUN_TEST)
+#define ATF_CLEANUP(i) atf_test(i, ATF_CLEANUP_TEST)
+
+#define atf_tc_set_md_var(tc, attr, fmt, ...) \
+ if (strcmp(attr, "descr") == 0) \
+ printf("DESCR=\"" fmt "\"\n", ##__VA_ARGS__); \
+ else if (strcmp(attr, "require.user") == 0) \
+ printf("REQ_USER=" fmt "\n", ##__VA_ARGS__);
+
+#define ATF_CHECK ATF_REQUIRE
+#define ATF_CHECK_MSG ATF_REQUIRE_MSG
+#define ATF_CHECK_EQ ATF_REQUIRE_EQ
+
+#define atf_req(exp, err, msg, ...) \
+ atf_require(exp, err, #exp, __FILE__, __LINE__, NULL)
+#define ATF_REQUIRE(exp) atf_req(exp, -1, NULL)
+#define ATF_REQUIRE_ERRNO(no, exp) atf_req(exp, no, NULL)
+#define ATF_REQUIRE_MSG(exp, fmt, ...) atf_req(exp, -1, fmt, ##__VA_ARGS__)
+#define ATF_REQUIRE_EQ(a, b) atf_req((a) == (b), -1, NULL)
+#define ATF_REQUIRE_EQ_MSG(a, b, fmt, ...) \
+ atf_req((a) == (b), -1, fmt, ##__VA_ARGS__)
+
+#define atf_tc_fail_nonfatal(fmt, ...) atf_tc_fail(fmt, ##__VA_ARGS__)
+#define atf_tc_expect_fail(fmt, ...) \
+ atf_tc_fail(fmt "\nEXPECTED_FAIL", ##__VA_ARGS__)
+#define atf_tc_skip(fmt, ...) \
+ atf_tc_fail(fmt "\nSKIPPING", ##__VA_ARGS__)
+#define atf_tc_pass() exit(0)
+
+#define atf_tc_get_config_var(a, b) "."
+
+#define atf_utils_fork() fork()
+
+#define atf_tc_get_config_var_as_bool_wd(tc, str, b) 0
+
+#endif /* !defined(ATF_C_H) */
diff --git a/regress/sys/kern/ptrace2/macros.h b/regress/sys/kern/ptrace2/macros.h
new file mode 100644
index 00000000000..2446a7272e0
--- /dev/null
+++ b/regress/sys/kern/ptrace2/macros.h
@@ -0,0 +1,68 @@
+/* $OpenBSD: macros.h,v 1.1 2020/02/28 12:48:30 mpi Exp $ */
+/* Public domain - Moritz Buhl */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stdint.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+
+#define __FBSDID(str)
+#define __RCSID(str)
+#define __COPYRIGHT(str)
+
+#define __arraycount(_a) nitems(_a)
+#define __unreachable() atf_tc_fail("unreachable")
+#define __UNCONST(a) (a)
+
+#define __dead2 __dead
+
+/* t_chroot.c */
+#define fchroot(fd) 0
+
+/* t_clock_gettime.c */
+int sysctlbyname(char *, void *, size_t *, void *, size_t);
+
+int
+sysctlbyname(char* s, void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+ int mib[3], miblen;
+
+ mib[0] = CTL_KERN;
+ if (strcmp(s, "kern.timecounter.hardware") == 0) {
+ mib[1] = KERN_TIMECOUNTER;
+ mib[2] = KERN_TIMECOUNTER_HARDWARE;
+ miblen = 3;
+ } else if (strcmp(s, "kern.timecounter.choice") == 0) {
+ mib[1] = KERN_TIMECOUNTER;
+ mib[2] = KERN_TIMECOUNTER_CHOICE;
+ miblen = 3;
+ } else if (strcmp(s, "kern.securelevel") == 0) {
+ mib[1] = KERN_SECURELVL;
+ miblen = 2;
+ } else {
+ fprintf(stderr, "%s(): mib '%s' not supported\n", __func__, s);
+ return -42;
+ }
+
+ return sysctl(mib, miblen, oldp, oldlenp, newp, newlen);
+}
+
+/* t_mlock.c */
+#define MAP_WIRED __MAP_NOREPLACE
+
+/* t_pipe2.c */
+#define O_NOSIGPIPE 0
+
+/* t_poll.c */
+#define pollts(a, b, c, e) 0
+
+/* t_sendrecv.c */
+#define SO_RERROR SO_DEBUG
+
+/* t_write.c */
+#define _PATH_DEVZERO "/dev/zero"
diff --git a/regress/sys/kern/ptrace2/ptrace_test.c b/regress/sys/kern/ptrace2/ptrace_test.c
new file mode 100644
index 00000000000..469f3fb51c6
--- /dev/null
+++ b/regress/sys/kern/ptrace2/ptrace_test.c
@@ -0,0 +1,232 @@
+/* $OpenBSD: ptrace_test.c,v 1.1 2020/02/28 12:48:30 mpi Exp $ */
+
+/*-
+ * Copyright (c) 2015 John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "macros.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/ptrace.h>
+#include <sys/queue.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <machine/cpufunc.h>
+#include <pthread.h>
+#include <sched.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "atf-c.h"
+
+/*
+ * A variant of ATF_REQUIRE that is suitable for use in child
+ * processes. This only works if the parent process is tripped up by
+ * the early exit and fails some requirement itself.
+ */
+#define CHILD_REQUIRE(exp) do { \
+ if (!(exp)) \
+ child_fail_require(__FILE__, __LINE__, \
+ #exp " not met"); \
+ } while (0)
+
+static __dead2 void
+child_fail_require(const char *file, int line, const char *str)
+{
+ char buf[128];
+
+ snprintf(buf, sizeof(buf), "%s:%d: %s\n", file, line, str);
+ write(2, buf, strlen(buf));
+ _exit(32);
+}
+
+static void
+trace_me(void)
+{
+
+ /* Attach the parent process as a tracer of this process. */
+ CHILD_REQUIRE(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ /* Trigger a stop. */
+ raise(SIGSTOP);
+}
+
+static void
+attach_child(pid_t pid)
+{
+ pid_t wpid;
+ int status;
+
+ ATF_REQUIRE(ptrace(PT_ATTACH, pid, NULL, 0) == 0);
+
+ wpid = waitpid(pid, &status, 0);
+ ATF_REQUIRE(wpid == pid);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
+}
+
+static void
+wait_for_zombie(pid_t pid)
+{
+
+ /*
+ * Wait for a process to exit. This is kind of gross, but
+ * there is not a better way.
+ *
+ * Prior to r325719, the kern.proc.pid.<pid> sysctl failed
+ * with ESRCH. After that change, a valid struct kinfo_proc
+ * is returned for zombies with ki_stat set to SZOMB.
+ */
+ for (;;) {
+ struct kinfo_proc kp;
+ size_t len;
+ int mib[6];
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = pid;
+ mib[4] = len = sizeof(kp);
+ mib[5] = 1;
+ if (sysctl(mib, nitems(mib), &kp, &len, NULL, 0) == -1) {
+ ATF_REQUIRE(errno == ESRCH);
+ break;
+ }
+ if (kp.p_stat == SDEAD)
+ break;
+ usleep(5000);
+ }
+}
+
+/*
+ * Verify that a parent debugger process "sees" the exit of a debugged
+ * process exactly once when attached via PT_TRACE_ME.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_trace_me);
+ATF_TC_BODY(ptrace__parent_wait_after_trace_me, tc)
+{
+ pid_t child, wpid;
+ int status;
+
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ /* Child process. */
+ trace_me();
+
+ _exit(1);
+ }
+
+ /* Parent process. */
+
+ /* The first wait() should report the stop from SIGSTOP. */
+ wpid = waitpid(child, &status, 0);
+ printf("first %d\n", wpid);
+ ATF_REQUIRE(wpid == child);
+ ATF_REQUIRE(WIFSTOPPED(status));
+ ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
+
+ /* The second wait() should report the exit status. */
+ wpid = waitpid(child, &status, 0);
+ printf("second %d\n", wpid);
+ ATF_REQUIRE(wpid == child);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 1);
+
+ /* The child should no longer exist. */
+ wpid = waitpid(child, &status, 0);
+ printf("third %d\n", wpid);
+ ATF_REQUIRE(wpid == -1);
+ ATF_REQUIRE(errno == ECHILD);
+}
+
+/*
+ * Verify that a parent debugger process "sees" the exit of a debugged
+ * process exactly once when attached via PT_ATTACH.
+ */
+ATF_TC_WITHOUT_HEAD(ptrace__parent_wait_after_attach);
+ATF_TC_BODY(ptrace__parent_wait_after_attach, tc)
+{
+ pid_t child, wpid;
+ int cpipe[2], status;
+ char c;
+
+ ATF_REQUIRE(pipe(cpipe) == 0);
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ /* Child process. */
+ close(cpipe[0]);
+
+ /* Wait for the parent to attach. */
+ CHILD_REQUIRE(read(cpipe[1], &c, sizeof(c)) == 0);
+
+ _exit(1);
+ }
+ close(cpipe[1]);
+
+ /* Parent process. */
+
+ /* Attach to the child process. */
+ attach_child(child);
+
+ /* Continue the child ignoring the SIGSTOP. */
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (caddr_t)1, 0) != -1);
+
+ /* Signal the child to exit. */
+ close(cpipe[0]);
+
+ /* The second wait() should report the exit status. */
+ wpid = waitpid(child, &status, 0);
+ ATF_REQUIRE(wpid == child);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 1);
+
+ /* The child should no longer exist. */
+ wpid = waitpid(child, &status, 0);
+ ATF_REQUIRE(wpid == -1);
+ ATF_REQUIRE(errno == ECHILD);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_trace_me);
+ ATF_TP_ADD_TC(tp, ptrace__parent_wait_after_attach);
+
+ return (atf_no_error());
+}