summaryrefslogtreecommitdiff
path: root/tests/lib/libc
diff options
context:
space:
mode:
authorkamil <kamil@NetBSD.org>2017-04-02 21:44:00 +0000
committerkamil <kamil@NetBSD.org>2017-04-02 21:44:00 +0000
commit7a727116a781da79fbf3626aede2f657123cb2f7 (patch)
tree985c963f311a7c4db2f488516ba5abfebe7cc43b /tests/lib/libc
parent4db5ba993503a0256eba8953bd373393c2fe42d4 (diff)
Import ptrace(2) tests into appropriate directory tests/lib/libc/sys/
This is the correct directory documented in tests/README for such tests. Discussed with <martin> Sponsored by <The NetBSD Foundation>
Diffstat (limited to 'tests/lib/libc')
-rw-r--r--tests/lib/libc/sys/Makefile17
-rw-r--r--tests/lib/libc/sys/msg.h136
-rw-r--r--tests/lib/libc/sys/t_ptrace.c208
-rw-r--r--tests/lib/libc/sys/t_ptrace_amd64_wait.h114
-rw-r--r--tests/lib/libc/sys/t_ptrace_i386_wait.h107
-rw-r--r--tests/lib/libc/sys/t_ptrace_wait.c7590
-rw-r--r--tests/lib/libc/sys/t_ptrace_wait.h431
-rw-r--r--tests/lib/libc/sys/t_ptrace_wait3.c30
-rw-r--r--tests/lib/libc/sys/t_ptrace_wait4.c30
-rw-r--r--tests/lib/libc/sys/t_ptrace_wait6.c30
-rw-r--r--tests/lib/libc/sys/t_ptrace_waitid.c30
-rw-r--r--tests/lib/libc/sys/t_ptrace_waitpid.c30
-rw-r--r--tests/lib/libc/sys/t_ptrace_x86_wait.h1983
13 files changed, 10734 insertions, 2 deletions
diff --git a/tests/lib/libc/sys/Makefile b/tests/lib/libc/sys/Makefile
index 6f2f59a86fd..e3f21b898da 100644
--- a/tests/lib/libc/sys/Makefile
+++ b/tests/lib/libc/sys/Makefile
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.48 2017/02/01 10:18:27 martin Exp $
+# $NetBSD: Makefile,v 1.49 2017/04/02 21:44:00 kamil Exp $
MKMAN= no
@@ -51,6 +51,13 @@ TESTS_C+= t_pipe
TESTS_C+= t_pipe2
TESTS_C+= t_poll
TESTS_C+= t_posix_fallocate
+TESTS_C+= t_ptrace
+TESTS_C+= t_ptrace_wait
+TESTS_C+= t_ptrace_wait3
+TESTS_C+= t_ptrace_wait4
+TESTS_C+= t_ptrace_wait6
+TESTS_C+= t_ptrace_waitid
+TESTS_C+= t_ptrace_waitpid
TESTS_C+= t_recvmmsg
TESTS_C+= t_revoke
TESTS_C+= t_select
@@ -82,7 +89,13 @@ TESTS_C+= t_posix_fadvise
LDADD.t_posix_fadvise+= -lrumpvfs -lrump -lrumpuser -lrump -lpthread
.endif
-CPPFLAGS.t_lwp_create.c += -D_KERNTYPES
+CPPFLAGS.t_lwp_create.c += -D_KERNTYPES
+CPPFLAGS.t_ptrace_wait.c += -D_KERNTYPES
+CPPFLAGS.t_ptrace_wait3.c += -D_KERNTYPES
+CPPFLAGS.t_ptrace_wait4.c += -D_KERNTYPES
+CPPFLAGS.t_ptrace_wait6.c += -D_KERNTYPES
+CPPFLAGS.t_ptrace_waitid.c += -D_KERNTYPES
+CPPFLAGS.t_ptrace_waitpid.c += -D_KERNTYPES
FILES= truncate_test.root_owned
FILESBUILD= yes
diff --git a/tests/lib/libc/sys/msg.h b/tests/lib/libc/sys/msg.h
new file mode 100644
index 00000000000..184949af0cf
--- /dev/null
+++ b/tests/lib/libc/sys/msg.h
@@ -0,0 +1,136 @@
+/* $NetBSD: msg.h,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+struct msg_fds {
+ int pfd[2];
+ int cfd[2];
+};
+
+#define CLOSEFD(fd) do { \
+ if (fd != -1) { \
+ close(fd); \
+ fd = -1; \
+ } \
+} while (/*CONSTCOND*/ 0)
+
+static int
+msg_open(struct msg_fds *fds)
+{
+ if (pipe(fds->pfd) == -1)
+ return -1;
+ if (pipe(fds->cfd) == -1) {
+ close(fds->pfd[0]);
+ close(fds->pfd[1]);
+ return -1;
+ }
+ return 0;
+}
+
+static void
+msg_close(struct msg_fds *fds)
+{
+ CLOSEFD(fds->pfd[0]);
+ CLOSEFD(fds->pfd[1]);
+ CLOSEFD(fds->cfd[0]);
+ CLOSEFD(fds->cfd[1]);
+}
+
+static int
+msg_write_child(const char *info, struct msg_fds *fds, void *msg, size_t len)
+{
+ ssize_t rv;
+ CLOSEFD(fds->cfd[1]);
+ CLOSEFD(fds->pfd[0]);
+
+ printf("Send %s\n", info);
+ rv = write(fds->pfd[1], msg, len);
+ if (rv != (ssize_t)len)
+ return 1;
+// printf("Wait %s\n", info);
+ rv = read(fds->cfd[0], msg, len);
+ if (rv != (ssize_t)len)
+ return 1;
+ return 0;
+}
+
+static int
+msg_write_parent(const char *info, struct msg_fds *fds, void *msg, size_t len)
+{
+ ssize_t rv;
+ CLOSEFD(fds->pfd[1]);
+ CLOSEFD(fds->cfd[0]);
+
+ printf("Send %s\n", info);
+ rv = write(fds->cfd[1], msg, len);
+ if (rv != (ssize_t)len)
+ return 1;
+// printf("Wait %s\n", info);
+ rv = read(fds->pfd[0], msg, len);
+ if (rv != (ssize_t)len)
+ return 1;
+ return 0;
+}
+
+static int
+msg_read_parent(const char *info, struct msg_fds *fds, void *msg, size_t len)
+{
+ ssize_t rv;
+ CLOSEFD(fds->pfd[1]);
+ CLOSEFD(fds->cfd[0]);
+
+ printf("Wait %s\n", info);
+ rv = read(fds->pfd[0], msg, len);
+ if (rv != (ssize_t)len)
+ return 1;
+// printf("Send %s\n", info);
+ rv = write(fds->cfd[1], msg, len);
+ if (rv != (ssize_t)len)
+ return 1;
+ return 0;
+}
+
+static int
+msg_read_child(const char *info, struct msg_fds *fds, void *msg, size_t len)
+{
+ ssize_t rv;
+ CLOSEFD(fds->cfd[1]);
+ CLOSEFD(fds->pfd[0]);
+
+ printf("Wait %s\n", info);
+ rv = read(fds->cfd[0], msg, len);
+ if (rv != (ssize_t)len)
+ return 1;
+// printf("Send %s\n", info);
+ rv = write(fds->pfd[1], msg, len);
+ if (rv != (ssize_t)len)
+ return 1;
+ return 0;
+}
diff --git a/tests/lib/libc/sys/t_ptrace.c b/tests/lib/libc/sys/t_ptrace.c
new file mode 100644
index 00000000000..76b1ac9c29b
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace.c
@@ -0,0 +1,208 @@
+/* $NetBSD: t_ptrace.c,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/cdefs.h>
+__RCSID("$NetBSD: t_ptrace.c,v 1.1 2017/04/02 21:44:00 kamil Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "h_macros.h"
+
+/*
+ * A child process cannot call atf functions and expect them to magically
+ * work like in the parent.
+ * The printf(3) messaging from a child will not work out of the box as well
+ * without estabilishing a communication protocol with its parent. To not
+ * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
+ * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work.
+ */
+#define FORKEE_ASSERTX(x) \
+do { \
+ int ret = (x); \
+ if (!ret) \
+ errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
+ __FILE__, __LINE__, __func__, #x); \
+} while (0)
+
+#define FORKEE_ASSERT(x) \
+do { \
+ int ret = (x); \
+ if (!ret) \
+ err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s", \
+ __FILE__, __LINE__, __func__, #x); \
+} while (0)
+
+ATF_TC(attach_pid0);
+ATF_TC_HEAD(attach_pid0, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that a debugger cannot attach to PID 0");
+}
+
+ATF_TC_BODY(attach_pid0, tc)
+{
+ errno = 0;
+ ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 0, NULL, 0) == -1);
+}
+
+ATF_TC(attach_pid1);
+ATF_TC_HEAD(attach_pid1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that a debugger cannot attach to PID 1 (as non-root)");
+
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+
+ATF_TC_BODY(attach_pid1, tc)
+{
+ ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1);
+}
+
+ATF_TC(attach_pid1_securelevel);
+ATF_TC_HEAD(attach_pid1_securelevel, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that a debugger cannot attach to PID 1 with "
+ "securelevel >= 0 (as root)");
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(attach_pid1_securelevel, tc)
+{
+ int level;
+ size_t len = sizeof(level);
+
+ ATF_REQUIRE(sysctlbyname("kern.securelevel", &level, &len, NULL, 0)
+ != -1);
+
+ if (level < 0) {
+ atf_tc_skip("Test must be run with securelevel >= 0");
+ }
+
+ ATF_REQUIRE_ERRNO(EPERM, ptrace(PT_ATTACH, 1, NULL, 0) == -1);
+}
+
+ATF_TC(attach_self);
+ATF_TC_HEAD(attach_self, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that a debugger cannot attach to self (as it's nonsense)");
+}
+
+ATF_TC_BODY(attach_self, tc)
+{
+ ATF_REQUIRE_ERRNO(EINVAL, ptrace(PT_ATTACH, getpid(), NULL, 0) == -1);
+}
+
+ATF_TC(attach_chroot);
+ATF_TC_HEAD(attach_chroot, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that a debugger cannot trace another process unless the "
+ "process's root directory is at or below the tracing process's "
+ "root");
+
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+
+ATF_TC_BODY(attach_chroot, tc)
+{
+ char buf[PATH_MAX];
+ pid_t child;
+ int fds_toparent[2], fds_fromparent[2];
+ int rv;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+
+ (void)memset(buf, '\0', sizeof(buf));
+ ATF_REQUIRE(getcwd(buf, sizeof(buf)) != NULL);
+ (void)strlcat(buf, "/dir", sizeof(buf));
+
+ ATF_REQUIRE(mkdir(buf, 0500) == 0);
+ ATF_REQUIRE(chdir(buf) == 0);
+
+ ATF_REQUIRE(pipe(fds_toparent) == 0);
+ ATF_REQUIRE(pipe(fds_fromparent) == 0);
+ child = atf_utils_fork();
+ if (child == 0) {
+ FORKEE_ASSERT(close(fds_toparent[0]) == 0);
+ FORKEE_ASSERT(close(fds_fromparent[1]) == 0);
+
+ FORKEE_ASSERT(chroot(buf) == 0);
+
+ rv = write(fds_toparent[1], &msg, sizeof(msg));
+ FORKEE_ASSERTX(rv == sizeof(msg));
+
+ ATF_REQUIRE_ERRNO(EPERM,
+ ptrace(PT_ATTACH, getppid(), NULL, 0) == -1);
+
+ rv = read(fds_fromparent[0], &msg, sizeof(msg));
+ FORKEE_ASSERTX(rv == sizeof(msg));
+
+ _exit(0);
+ }
+ ATF_REQUIRE(close(fds_toparent[1]) == 0);
+ ATF_REQUIRE(close(fds_fromparent[0]) == 0);
+
+ printf("Waiting for chrooting of the child PID %d", child);
+ rv = read(fds_toparent[0], &msg, sizeof(msg));
+ ATF_REQUIRE(rv == sizeof(msg));
+
+ printf("Child is ready, it will try to PT_ATTACH to parent\n");
+ rv = write(fds_fromparent[1], &msg, sizeof(msg));
+ ATF_REQUIRE(rv == sizeof(msg));
+
+ printf("fds_fromparent is no longer needed - close it\n");
+ ATF_REQUIRE(close(fds_fromparent[1]) == 0);
+
+ printf("fds_toparent is no longer needed - close it\n");
+ ATF_REQUIRE(close(fds_toparent[0]) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stderr, NULL, _IONBF, 0);
+ ATF_TP_ADD_TC(tp, attach_pid0);
+ ATF_TP_ADD_TC(tp, attach_pid1);
+ ATF_TP_ADD_TC(tp, attach_pid1_securelevel);
+ ATF_TP_ADD_TC(tp, attach_self);
+ ATF_TP_ADD_TC(tp, attach_chroot);
+
+ return atf_no_error();
+}
diff --git a/tests/lib/libc/sys/t_ptrace_amd64_wait.h b/tests/lib/libc/sys/t_ptrace_amd64_wait.h
new file mode 100644
index 00000000000..62ce21ae66b
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_amd64_wait.h
@@ -0,0 +1,114 @@
+/* $NetBSD: t_ptrace_amd64_wait.h,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if defined(__x86_64__)
+ATF_TC(x86_64_regs1);
+ATF_TC_HEAD(x86_64_regs1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Call PT_GETREGS and iterate over General Purpose registers");
+}
+
+ATF_TC_BODY(x86_64_regs1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct reg r;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1);
+
+ printf("RAX=%#" PRIxREGISTER "\n", r.regs[_REG_RAX]);
+ printf("RBX=%#" PRIxREGISTER "\n", r.regs[_REG_RBX]);
+ printf("RCX=%#" PRIxREGISTER "\n", r.regs[_REG_RCX]);
+ printf("RDX=%#" PRIxREGISTER "\n", r.regs[_REG_RDX]);
+
+ printf("RDI=%#" PRIxREGISTER "\n", r.regs[_REG_RDI]);
+ printf("RSI=%#" PRIxREGISTER "\n", r.regs[_REG_RSI]);
+
+ printf("GS=%#" PRIxREGISTER "\n", r.regs[_REG_GS]);
+ printf("FS=%#" PRIxREGISTER "\n", r.regs[_REG_FS]);
+ printf("ES=%#" PRIxREGISTER "\n", r.regs[_REG_ES]);
+ printf("DS=%#" PRIxREGISTER "\n", r.regs[_REG_DS]);
+ printf("CS=%#" PRIxREGISTER "\n", r.regs[_REG_CS]);
+ printf("SS=%#" PRIxREGISTER "\n", r.regs[_REG_SS]);
+
+ printf("RSP=%#" PRIxREGISTER "\n", r.regs[_REG_RSP]);
+ printf("RIP=%#" PRIxREGISTER "\n", r.regs[_REG_RIP]);
+
+ printf("RFLAGS=%#" PRIxREGISTER "\n", r.regs[_REG_RFLAGS]);
+
+ printf("R8=%#" PRIxREGISTER "\n", r.regs[_REG_R8]);
+ printf("R9=%#" PRIxREGISTER "\n", r.regs[_REG_R9]);
+ printf("R10=%#" PRIxREGISTER "\n", r.regs[_REG_R10]);
+ printf("R11=%#" PRIxREGISTER "\n", r.regs[_REG_R11]);
+ printf("R12=%#" PRIxREGISTER "\n", r.regs[_REG_R12]);
+ printf("R13=%#" PRIxREGISTER "\n", r.regs[_REG_R13]);
+ printf("R14=%#" PRIxREGISTER "\n", r.regs[_REG_R14]);
+ printf("R15=%#" PRIxREGISTER "\n", r.regs[_REG_R15]);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#define ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64() \
+ ATF_TP_ADD_TC_HAVE_GPREGS(tp, x86_64_regs1);
+#else
+#define ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64()
+#endif
diff --git a/tests/lib/libc/sys/t_ptrace_i386_wait.h b/tests/lib/libc/sys/t_ptrace_i386_wait.h
new file mode 100644
index 00000000000..eb7823d987b
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_i386_wait.h
@@ -0,0 +1,107 @@
+/* $NetBSD: t_ptrace_i386_wait.h,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if defined(__i386__)
+ATF_TC(i386_regs1);
+ATF_TC_HEAD(i386_regs1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Call PT_GETREGS and iterate over General Purpose registers");
+}
+
+ATF_TC_BODY(i386_regs1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct reg r;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1);
+
+ printf("EAX=%#" PRIxREGISTER "\n", r.r_eax);
+ printf("EBX=%#" PRIxREGISTER "\n", r.r_ebx);
+ printf("ECX=%#" PRIxREGISTER "\n", r.r_ecx);
+ printf("EDX=%#" PRIxREGISTER "\n", r.r_edx);
+
+ printf("ESP=%#" PRIxREGISTER "\n", r.r_esp);
+ printf("EBP=%#" PRIxREGISTER "\n", r.r_ebp);
+
+ printf("ESI=%#" PRIxREGISTER "\n", r.r_esi);
+ printf("EDI=%#" PRIxREGISTER "\n", r.r_edi);
+
+ printf("EIP=%#" PRIxREGISTER "\n", r.r_eip);
+
+ printf("EFLAGS=%#" PRIxREGISTER "\n", r.r_eflags);
+
+ printf("CS=%#" PRIxREGISTER "\n", r.r_cs);
+ printf("SS=%#" PRIxREGISTER "\n", r.r_ss);
+ printf("DS=%#" PRIxREGISTER "\n", r.r_ds);
+ printf("ES=%#" PRIxREGISTER "\n", r.r_es);
+ printf("FS=%#" PRIxREGISTER "\n", r.r_fs);
+ printf("GS=%#" PRIxREGISTER "\n", r.r_gs);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#define ATF_TP_ADD_TCS_PTRACE_WAIT_I386() \
+ ATF_TP_ADD_TC_HAVE_GPREGS(tp, i386_regs1);
+#else
+#define ATF_TP_ADD_TCS_PTRACE_WAIT_I386()
+#endif
diff --git a/tests/lib/libc/sys/t_ptrace_wait.c b/tests/lib/libc/sys/t_ptrace_wait.c
new file mode 100644
index 00000000000..c60a125bf1b
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_wait.c
@@ -0,0 +1,7590 @@
+/* $NetBSD: t_ptrace_wait.c,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/cdefs.h>
+__RCSID("$NetBSD: t_ptrace_wait.c,v 1.1 2017/04/02 21:44:00 kamil Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <machine/reg.h>
+#include <elf.h>
+#include <err.h>
+#include <errno.h>
+#include <lwp.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "h_macros.h"
+
+#include "t_ptrace_wait.h"
+#include "msg.h"
+
+#define PARENT_TO_CHILD(info, fds, msg) \
+ ATF_REQUIRE(msg_write_child(info " to child " # fds, &fds, &msg, sizeof(msg)) == 0)
+
+#define CHILD_FROM_PARENT(info, fds, msg) \
+ FORKEE_ASSERT(msg_read_parent(info " from parent " # fds, &fds, &msg, sizeof(msg)) == 0)
+
+#define CHILD_TO_PARENT(info, fds, msg) \
+ FORKEE_ASSERT(msg_write_parent(info " to parent " # fds, &fds, &msg, sizeof(msg)) == 0)
+
+#define PARENT_FROM_CHILD(info, fds, msg) \
+ ATF_REQUIRE(msg_read_child(info " from parent " # fds, &fds, &msg, sizeof(msg)) == 0)
+
+
+ATF_TC(traceme1);
+ATF_TC_HEAD(traceme1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify SIGSTOP followed by _exit(2) in a child");
+}
+
+ATF_TC_BODY(traceme1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(traceme2);
+ATF_TC_HEAD(traceme2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify SIGSTOP followed by _exit(2) in a child");
+}
+
+static int traceme2_caught = 0;
+
+static void
+traceme2_sighandler(int sig)
+{
+ FORKEE_ASSERT_EQ(sig, SIGINT);
+
+ ++traceme2_caught;
+}
+
+ATF_TC_BODY(traceme2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP, sigsent = SIGINT;
+ pid_t child, wpid;
+ struct sigaction sa;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sa.sa_handler = traceme2_sighandler;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+
+ FORKEE_ASSERT(sigaction(sigsent, &sa, NULL) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(traceme2_caught, 1);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and with "
+ "signal %s to be sent\n", strsignal(sigsent));
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the exited child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(traceme3);
+ATF_TC_HEAD(traceme3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify SIGSTOP followed by termination by a signal in a child");
+}
+
+ATF_TC_BODY(traceme3, tc)
+{
+ const int sigval = SIGSTOP, sigsent = SIGINT /* Without core-dump */;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ /* NOTREACHED */
+ FORKEE_ASSERTX(0 &&
+ "Child should be terminated by a signal from its parent");
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and with "
+ "signal %s to be sent\n", strsignal(sigsent));
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_signaled(status, sigsent, 0);
+
+ printf("Before calling %s() for the exited child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(traceme4);
+ATF_TC_HEAD(traceme4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify SIGSTOP followed by SIGCONT and _exit(2) in a child");
+}
+
+ATF_TC_BODY(traceme4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP, sigsent = SIGCONT;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before raising %s from child\n", strsignal(sigsent));
+ FORKEE_ASSERT(raise(sigsent) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(),child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigsent);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the exited child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(attach1);
+ATF_TC_HEAD(attach1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that tracer sees process termination before the parent");
+}
+
+ATF_TC_BODY(attach1, tc)
+{
+ struct msg_fds parent_tracee, parent_tracer;
+ const int exitval_tracee = 5;
+ const int exitval_tracer = 10;
+ pid_t tracee, tracer, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Spawn tracee\n");
+ ATF_REQUIRE(msg_open(&parent_tracee) == 0);
+ tracee = atf_utils_fork();
+ if (tracee == 0) {
+ // Wait for parent to let us exit
+ CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
+ _exit(exitval_tracee);
+ }
+
+ printf("Spawn debugger\n");
+ ATF_REQUIRE(msg_open(&parent_tracer) == 0);
+ tracer = atf_utils_fork();
+ if (tracer == 0) {
+ printf("Before calling PT_ATTACH from tracee %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+ /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_stopped(status, SIGSTOP);
+
+ /* Resume tracee with PT_CONTINUE */
+ FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+ /* Inform parent that tracer has attached to tracee */
+ CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
+
+ /* Wait for parent to tell use that tracee should have exited */
+ CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
+
+ /* Wait for tracee and assert that it exited */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_exited(status, exitval_tracee);
+ printf("Tracee %d exited with %d\n", tracee, exitval_tracee);
+
+ printf("Before exiting of the tracer process\n");
+ _exit(exitval_tracer);
+ }
+
+ printf("Wait for the tracer to attach to the tracee\n");
+ PARENT_FROM_CHILD("tracer ready", parent_tracer, msg);
+
+ printf("Resume the tracee and let it exit\n");
+ PARENT_TO_CHILD("exit tracee", parent_tracee, msg);
+
+ printf("Detect that tracee is zombie\n");
+ await_zombie(tracee);
+
+
+ printf("Assert that there is no status about tracee %d - "
+ "Tracer must detect zombie first - calling %s()\n", tracee,
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
+
+ printf("Tell the tracer child should have exited\n");
+ PARENT_TO_CHILD("wait for tracee exit", parent_tracer, msg);
+ printf("Wait for tracer to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+
+ printf("Wait from tracer child to complete waiting for tracee\n");
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
+ tracer);
+
+ validate_status_exited(status, exitval_tracer);
+
+ printf("Wait for tracee to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
+ tracee);
+
+ validate_status_exited(status, exitval_tracee);
+
+ msg_close(&parent_tracer);
+ msg_close(&parent_tracee);
+}
+#endif
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(attach2);
+ATF_TC_HEAD(attach2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that any tracer sees process termination before its "
+ "parent");
+}
+
+ATF_TC_BODY(attach2, tc)
+{
+ struct msg_fds parent_tracer, parent_tracee;
+ const int exitval_tracee = 5;
+ const int exitval_tracer1 = 10, exitval_tracer2 = 20;
+ pid_t tracee, tracer, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Spawn tracee\n");
+ ATF_REQUIRE(msg_open(&parent_tracee) == 0);
+ tracee = atf_utils_fork();
+ if (tracee == 0) {
+ /* Wait for message from the parent */
+ CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
+ _exit(exitval_tracee);
+ }
+
+ printf("Spawn debugger\n");
+ ATF_REQUIRE(msg_open(&parent_tracer) == 0);
+ tracer = atf_utils_fork();
+ if (tracer == 0) {
+ /* Fork again and drop parent to reattach to PID 1 */
+ tracer = atf_utils_fork();
+ if (tracer != 0)
+ _exit(exitval_tracer1);
+
+ printf("Before calling PT_ATTACH from tracee %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+ /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_stopped(status, SIGSTOP);
+
+ /* Resume tracee with PT_CONTINUE */
+ FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+ /* Inform parent that tracer has attached to tracee */
+ CHILD_TO_PARENT("Message 1", parent_tracer, msg);
+ CHILD_FROM_PARENT("Message 2", parent_tracer, msg);
+
+ /* Wait for tracee and assert that it exited */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_exited(status, exitval_tracee);
+
+ printf("Before exiting of the tracer process\n");
+ _exit(exitval_tracer2);
+ }
+ printf("Wait for the tracer process (direct child) to exit calling "
+ "%s()\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
+
+ validate_status_exited(status, exitval_tracer1);
+
+ printf("Wait for the non-exited tracee process with %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, NULL, WNOHANG), 0);
+
+ printf("Wait for the tracer to attach to the tracee\n");
+ PARENT_FROM_CHILD("Message 1", parent_tracer, msg);
+ printf("Resume the tracee and let it exit\n");
+ PARENT_TO_CHILD("Message 1", parent_tracee, msg);
+
+ printf("Detect that tracee is zombie\n");
+ await_zombie(tracee);
+
+ printf("Assert that there is no status about tracee - "
+ "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
+
+ printf("Resume the tracer and let it detect exited tracee\n");
+ PARENT_TO_CHILD("Message 2", parent_tracer, msg);
+
+ printf("Wait for tracee to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
+ tracee);
+
+ validate_status_exited(status, exitval_tracee);
+
+ msg_close(&parent_tracer);
+ msg_close(&parent_tracee);
+
+}
+#endif
+
+ATF_TC(attach3);
+ATF_TC_HEAD(attach3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that tracer parent can PT_ATTACH to its child");
+}
+
+ATF_TC_BODY(attach3, tc)
+{
+ struct msg_fds parent_tracee;
+ const int exitval_tracee = 5;
+ pid_t tracee, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Spawn tracee\n");
+ ATF_REQUIRE(msg_open(&parent_tracee) == 0);
+ tracee = atf_utils_fork();
+ if (tracee == 0) {
+ CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
+ printf("Parent should now attach to tracee\n");
+
+ CHILD_FROM_PARENT("Message 2", parent_tracee, msg);
+ /* Wait for message from the parent */
+ _exit(exitval_tracee);
+ }
+ PARENT_TO_CHILD("Message 1", parent_tracee, msg);
+
+ printf("Before calling PT_ATTACH for tracee %d\n", tracee);
+ ATF_REQUIRE(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+ printf("Wait for the stopped tracee process with %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ validate_status_stopped(status, SIGSTOP);
+
+ printf("Resume tracee with PT_CONTINUE\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+ printf("Let the tracee exit now\n");
+ PARENT_TO_CHILD("Message 2", parent_tracee, msg);
+
+ printf("Wait for tracee to exit with %s()\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ validate_status_exited(status, exitval_tracee);
+
+ printf("Before calling %s() for tracee\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD,
+ wpid = TWAIT_GENERIC(tracee, &status, 0));
+
+ msg_close(&parent_tracee);
+}
+
+ATF_TC(attach4);
+ATF_TC_HEAD(attach4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that tracer child can PT_ATTACH to its parent");
+}
+
+ATF_TC_BODY(attach4, tc)
+{
+ struct msg_fds parent_tracee;
+ const int exitval_tracer = 5;
+ pid_t tracer, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Spawn tracer\n");
+ ATF_REQUIRE(msg_open(&parent_tracee) == 0);
+ tracer = atf_utils_fork();
+ if (tracer == 0) {
+
+ /* Wait for message from the parent */
+ CHILD_FROM_PARENT("Message 1", parent_tracee, msg);
+
+ printf("Attach to parent PID %d with PT_ATTACH from child\n",
+ getppid());
+ FORKEE_ASSERT(ptrace(PT_ATTACH, getppid(), NULL, 0) != -1);
+
+ printf("Wait for the stopped parent process with %s()\n",
+ TWAIT_FNAME);
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(getppid(), &status, 0), getppid());
+
+ forkee_status_stopped(status, SIGSTOP);
+
+ printf("Resume parent with PT_DETACH\n");
+ FORKEE_ASSERT(ptrace(PT_DETACH, getppid(), (void *)1, 0)
+ != -1);
+
+ /* Tell parent we are ready */
+ CHILD_TO_PARENT("Message 1", parent_tracee, msg);
+
+ _exit(exitval_tracer);
+ }
+
+ printf("Wait for the tracer to become ready\n");
+ PARENT_TO_CHILD("Message 1", parent_tracee, msg);
+ printf("Allow the tracer to exit now\n");
+ PARENT_FROM_CHILD("Message 1", parent_tracee, msg);
+
+ printf("Wait for tracer to exit with %s()\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracer, &status, 0), tracer);
+
+ validate_status_exited(status, exitval_tracer);
+
+ printf("Before calling %s() for tracer\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD,
+ wpid = TWAIT_GENERIC(tracer, &status, 0));
+
+ msg_close(&parent_tracee);
+}
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(attach5);
+ATF_TC_HEAD(attach5, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that tracer sees its parent when attached to tracer "
+ "(check getppid(2))");
+}
+
+ATF_TC_BODY(attach5, tc)
+{
+ struct msg_fds parent_tracer, parent_tracee;
+ const int exitval_tracee = 5;
+ const int exitval_tracer = 10;
+ pid_t parent, tracee, tracer, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Spawn tracee\n");
+ ATF_REQUIRE(msg_open(&parent_tracer) == 0);
+ ATF_REQUIRE(msg_open(&parent_tracee) == 0);
+ tracee = atf_utils_fork();
+ if (tracee == 0) {
+ parent = getppid();
+
+ /* Emit message to the parent */
+ CHILD_TO_PARENT("tracee ready", parent_tracee, msg);
+ CHILD_FROM_PARENT("exit tracee", parent_tracee, msg);
+
+ FORKEE_ASSERT_EQ(parent, getppid());
+
+ _exit(exitval_tracee);
+ }
+ printf("Wait for child to record its parent identifier (pid)\n");
+ PARENT_FROM_CHILD("tracee ready", parent_tracee, msg);
+
+ printf("Spawn debugger\n");
+ tracer = atf_utils_fork();
+ if (tracer == 0) {
+ /* No IPC to communicate with the child */
+ printf("Before calling PT_ATTACH from tracee %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+ /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_stopped(status, SIGSTOP);
+
+ /* Resume tracee with PT_CONTINUE */
+ FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+ /* Inform parent that tracer has attached to tracee */
+ CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
+
+ /* Wait for parent to tell use that tracee should have exited */
+ CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
+
+ /* Wait for tracee and assert that it exited */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_exited(status, exitval_tracee);
+
+ printf("Before exiting of the tracer process\n");
+ _exit(exitval_tracer);
+ }
+
+ printf("Wait for the tracer to attach to the tracee\n");
+ PARENT_FROM_CHILD("tracer ready", parent_tracer, msg);
+
+ printf("Resume the tracee and let it exit\n");
+ PARENT_TO_CHILD("exit tracee", parent_tracee, msg);
+
+ printf("Detect that tracee is zombie\n");
+ await_zombie(tracee);
+
+ printf("Assert that there is no status about tracee - "
+ "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
+
+ printf("Tell the tracer child should have exited\n");
+ PARENT_TO_CHILD("wait for tracee exit", parent_tracer, msg);
+
+ printf("Wait from tracer child to complete waiting for tracee\n");
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
+ tracer);
+
+ validate_status_exited(status, exitval_tracer);
+
+ printf("Wait for tracee to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
+ tracee);
+
+ validate_status_exited(status, exitval_tracee);
+
+ msg_close(&parent_tracer);
+ msg_close(&parent_tracee);
+}
+#endif
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(attach6);
+ATF_TC_HEAD(attach6, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that tracer sees its parent when attached to tracer "
+ "(check sysctl(7) and struct kinfo_proc2)");
+}
+
+ATF_TC_BODY(attach6, tc)
+{
+ struct msg_fds parent_tracee, parent_tracer;
+ const int exitval_tracee = 5;
+ const int exitval_tracer = 10;
+ pid_t parent, tracee, tracer, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ int name[CTL_MAXNAME];
+ struct kinfo_proc2 kp;
+ size_t len = sizeof(kp);
+ unsigned int namelen;
+
+ printf("Spawn tracee\n");
+ ATF_REQUIRE(msg_open(&parent_tracee) == 0);
+ ATF_REQUIRE(msg_open(&parent_tracer) == 0);
+ tracee = atf_utils_fork();
+ if (tracee == 0) {
+ parent = getppid();
+
+ /* Emit message to the parent */
+ CHILD_TO_PARENT("Message 1", parent_tracee, msg);
+ CHILD_FROM_PARENT("Message 2", parent_tracee, msg);
+
+ namelen = 0;
+ name[namelen++] = CTL_KERN;
+ name[namelen++] = KERN_PROC2;
+ name[namelen++] = KERN_PROC_PID;
+ name[namelen++] = getpid();
+ name[namelen++] = len;
+ name[namelen++] = 1;
+
+ FORKEE_ASSERT(sysctl(name, namelen, &kp, &len, NULL, 0) == 0);
+ FORKEE_ASSERT_EQ(parent, kp.p_ppid);
+
+ _exit(exitval_tracee);
+ }
+
+ printf("Wait for child to record its parent identifier (pid)\n");
+ PARENT_FROM_CHILD("Message 1", parent_tracee, msg);
+
+ printf("Spawn debugger\n");
+ tracer = atf_utils_fork();
+ if (tracer == 0) {
+ /* No IPC to communicate with the child */
+ printf("Before calling PT_ATTACH from tracee %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+ /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_stopped(status, SIGSTOP);
+
+ /* Resume tracee with PT_CONTINUE */
+ FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+ /* Inform parent that tracer has attached to tracee */
+ CHILD_TO_PARENT("Message 1", parent_tracer, msg);
+
+ CHILD_FROM_PARENT("Message 2", parent_tracer, msg);
+
+ /* Wait for tracee and assert that it exited */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_exited(status, exitval_tracee);
+
+ printf("Before exiting of the tracer process\n");
+ _exit(exitval_tracer);
+ }
+
+ printf("Wait for the tracer to attach to the tracee\n");
+ PARENT_FROM_CHILD("Message 1", parent_tracer, msg);
+
+ printf("Resume the tracee and let it exit\n");
+ PARENT_TO_CHILD("Message 1", parent_tracee, msg);
+
+ printf("Detect that tracee is zombie\n");
+ await_zombie(tracee);
+
+ printf("Assert that there is no status about tracee - "
+ "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
+
+ printf("Resume the tracer and let it detect exited tracee\n");
+ PARENT_TO_CHILD("Message 2", parent_tracer, msg);
+
+ printf("Wait for tracer to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
+ tracer);
+
+ validate_status_exited(status, exitval_tracer);
+
+ printf("Wait for tracee to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
+ tracee);
+
+ validate_status_exited(status, exitval_tracee);
+
+ msg_close(&parent_tracee);
+ msg_close(&parent_tracer);
+}
+#endif
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(attach7);
+ATF_TC_HEAD(attach7, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Assert that tracer sees its parent when attached to tracer "
+ "(check /proc/curproc/status 3rd column)");
+}
+
+ATF_TC_BODY(attach7, tc)
+{
+ struct msg_fds parent_tracee, parent_tracer;
+ int rv;
+ const int exitval_tracee = 5;
+ const int exitval_tracer = 10;
+ pid_t parent, tracee, tracer, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ FILE *fp;
+ struct stat st;
+ const char *fname = "/proc/curproc/status";
+ char s_executable[MAXPATHLEN];
+ int s_pid, s_ppid;
+ /*
+ * Format:
+ * EXECUTABLE PID PPID ...
+ */
+
+ ATF_REQUIRE((rv = stat(fname, &st)) == 0 || (errno == ENOENT));
+ if (rv != 0) {
+ atf_tc_skip("/proc/curproc/status not found");
+ }
+
+ printf("Spawn tracee\n");
+ ATF_REQUIRE(msg_open(&parent_tracee) == 0);
+ ATF_REQUIRE(msg_open(&parent_tracer) == 0);
+ tracee = atf_utils_fork();
+ if (tracee == 0) {
+ parent = getppid();
+
+ // Wait for parent to let us exit
+ CHILD_TO_PARENT("tracee ready", parent_tracee, msg);
+ CHILD_FROM_PARENT("tracee exit", parent_tracee, msg);
+
+ FORKEE_ASSERT((fp = fopen(fname, "r")) != NULL);
+ fscanf(fp, "%s %d %d", s_executable, &s_pid, &s_ppid);
+ FORKEE_ASSERT(fclose(fp) == 0);
+ FORKEE_ASSERT_EQ(parent, s_ppid);
+
+ _exit(exitval_tracee);
+ }
+
+ printf("Wait for child to record its parent identifier (pid)\n");
+ PARENT_FROM_CHILD("tracee ready", parent_tracee, msg);
+
+ printf("Spawn debugger\n");
+ tracer = atf_utils_fork();
+ if (tracer == 0) {
+ printf("Before calling PT_ATTACH from tracee %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+ /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_stopped(status, SIGSTOP);
+
+ /* Resume tracee with PT_CONTINUE */
+ FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+ /* Inform parent that tracer has attached to tracee */
+ CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
+
+ /* Wait for parent to tell use that tracee should have exited */
+ CHILD_FROM_PARENT("wait for tracee exit", parent_tracer, msg);
+
+ /* Wait for tracee and assert that it exited */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_exited(status, exitval_tracee);
+
+ printf("Before exiting of the tracer process\n");
+ _exit(exitval_tracer);
+ }
+ printf("Wait for the tracer to attach to the tracee\n");
+ PARENT_FROM_CHILD("tracer ready", parent_tracer, msg);
+ printf("Resume the tracee and let it exit\n");
+ PARENT_TO_CHILD("tracee exit", parent_tracee, msg);
+
+ printf("Detect that tracee is zombie\n");
+ await_zombie(tracee);
+
+ printf("Assert that there is no status about tracee - "
+ "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
+
+ printf("Resume the tracer and let it detect exited tracee\n");
+ PARENT_TO_CHILD("Message 2", parent_tracer, msg);
+
+ printf("Wait for tracer to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
+ tracer);
+
+ validate_status_exited(status, exitval_tracer);
+
+ printf("Wait for tracee to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
+ tracee);
+
+ validate_status_exited(status, exitval_tracee);
+
+ msg_close(&parent_tracee);
+ msg_close(&parent_tracer);
+}
+#endif
+
+ATF_TC(eventmask1);
+ATF_TC_HEAD(eventmask1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that empty EVENT_MASK is preserved");
+}
+
+ATF_TC_BODY(eventmask1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_event_t set_event, get_event;
+ const int len = sizeof(ptrace_event_t);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ set_event.pe_set_event = 0;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1);
+ ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(eventmask2);
+ATF_TC_HEAD(eventmask2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that PTRACE_FORK in EVENT_MASK is preserved");
+}
+
+ATF_TC_BODY(eventmask2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_event_t set_event, get_event;
+ const int len = sizeof(ptrace_event_t);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ set_event.pe_set_event = PTRACE_FORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1);
+ ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(eventmask3);
+ATF_TC_HEAD(eventmask3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that PTRACE_VFORK in EVENT_MASK is preserved");
+}
+
+ATF_TC_BODY(eventmask3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_event_t set_event, get_event;
+ const int len = sizeof(ptrace_event_t);
+
+ atf_tc_expect_fail("PR kern/51630");
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ set_event.pe_set_event = PTRACE_VFORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1);
+ ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(eventmask4);
+ATF_TC_HEAD(eventmask4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that PTRACE_VFORK_DONE in EVENT_MASK is preserved");
+}
+
+ATF_TC_BODY(eventmask4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_event_t set_event, get_event;
+ const int len = sizeof(ptrace_event_t);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ set_event.pe_set_event = PTRACE_VFORK_DONE;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1);
+ ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(eventmask5);
+ATF_TC_HEAD(eventmask5, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that PTRACE_LWP_CREATE in EVENT_MASK is preserved");
+}
+
+ATF_TC_BODY(eventmask5, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_event_t set_event, get_event;
+ const int len = sizeof(ptrace_event_t);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ set_event.pe_set_event = PTRACE_LWP_CREATE;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1);
+ ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(eventmask6);
+ATF_TC_HEAD(eventmask6, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that PTRACE_LWP_EXIT in EVENT_MASK is preserved");
+}
+
+ATF_TC_BODY(eventmask6, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_event_t set_event, get_event;
+ const int len = sizeof(ptrace_event_t);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ set_event.pe_set_event = PTRACE_LWP_EXIT;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &set_event, len) != -1);
+ ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, child, &get_event, len) != -1);
+ ATF_REQUIRE(memcmp(&set_event, &get_event, len) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(fork1);
+ATF_TC_HEAD(fork1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that fork(2) is intercepted by ptrace(2) with EVENT_MASK "
+ "set to PTRACE_FORK");
+}
+
+ATF_TC_BODY(fork1, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+
+#if defined(__sparc__) || defined(__alpha__) || defined(__arm__)
+ /* ptrace(2) PTRACE_FORK fails on some platforms */
+ atf_tc_expect_fail("PR kern/52117");
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = fork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Enable PTRACE_FORK in EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_FORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ printf("We expect two SIGTRAP events, for child %d (TRAP_CHLD, "
+ "pe_report_event=PTRACE_FORK, state.pe_other_pid=child2) and "
+ "for child2 (TRAP_CHLD, pe_report_event=PTRACE_FORK, "
+ "state.pe_other_pid=child)\n", child);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK);
+
+ child2 = state.pe_other_pid;
+ printf("Reported PTRACE_FORK event with forkee %d\n", child2);
+
+ printf("Before calling %s() for the forkee %d of the child %d\n",
+ TWAIT_FNAME, child2, child);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK);
+ ATF_REQUIRE_EQ(state.pe_other_pid, child);
+
+ printf("Before resuming the forkee process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the forkee - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_exited(status, exitval2);
+
+ printf("Before calling %s() for the forkee - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD,
+ wpid = TWAIT_GENERIC(child2, &status, 0));
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+ATF_TC(fork2);
+ATF_TC_HEAD(fork2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that fork(2) is not intercepted by ptrace(2) with empty "
+ "EVENT_MASK");
+}
+
+ATF_TC_BODY(fork2, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = fork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Set empty EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = 0;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(vfork1);
+ATF_TC_HEAD(vfork1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that vfork(2) is intercepted by ptrace(2) with EVENT_MASK "
+ "set to PTRACE_VFORK");
+}
+
+ATF_TC_BODY(vfork1, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+
+ atf_tc_expect_fail("PR kern/51630");
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = vfork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Enable PTRACE_VFORK in EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_VFORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK);
+
+ child2 = state.pe_other_pid;
+ printf("Reported PTRACE_VFORK event with forkee %d\n", child2);
+
+ printf("Before calling %s() for the forkee %d of the child %d\n",
+ TWAIT_FNAME, child2, child);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK);
+ ATF_REQUIRE_EQ(state.pe_other_pid, child);
+
+ printf("Before resuming the forkee process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the forkee - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_exited(status, exitval2);
+
+ printf("Before calling %s() for the forkee - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD,
+ wpid = TWAIT_GENERIC(child2, &status, 0));
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+ATF_TC(vfork2);
+ATF_TC_HEAD(vfork2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that vfork(2) is not intercepted by ptrace(2) with empty "
+ "EVENT_MASK");
+}
+
+ATF_TC_BODY(vfork2, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = vfork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Set empty EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = 0;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(vforkdone1);
+ATF_TC_HEAD(vforkdone1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that vfork(2) is intercepted by ptrace(2) with EVENT_MASK "
+ "set to PTRACE_VFORK_DONE");
+}
+
+ATF_TC_BODY(vforkdone1, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = vfork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Enable PTRACE_VFORK_DONE in EVENT_MASK for the child %d\n",
+ child);
+ event.pe_set_event = PTRACE_VFORK_DONE;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE);
+
+ child2 = state.pe_other_pid;
+ printf("Reported PTRACE_VFORK_DONE event with forkee %d\n", child2);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(vforkdone2);
+ATF_TC_HEAD(vforkdone2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that vfork(2) is intercepted by ptrace(2) with EVENT_MASK "
+ "set to PTRACE_FORK | PTRACE_VFORK_DONE");
+}
+
+ATF_TC_BODY(vforkdone2, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = vfork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Enable PTRACE_VFORK in EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_FORK | PTRACE_VFORK_DONE;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE);
+
+ child2 = state.pe_other_pid;
+ printf("Reported PTRACE_VFORK_DONE event with forkee %d\n", child2);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_d1);
+ATF_TC_HEAD(io_read_d1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_READ_D and len = sizeof(uint8_t)");
+}
+
+ATF_TC_BODY(io_read_d1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint8_t lookup_me = 0;
+ const uint8_t magic = 0xab;
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = &lookup_me,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me = magic;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %" PRIx8 " != expected %" PRIx8, lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_d2);
+ATF_TC_HEAD(io_read_d2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_READ_D and len = sizeof(uint16_t)");
+}
+
+ATF_TC_BODY(io_read_d2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint16_t lookup_me = 0;
+ const uint16_t magic = 0x1234;
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = &lookup_me,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me = magic;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %" PRIx16 " != expected %" PRIx16, lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_d3);
+ATF_TC_HEAD(io_read_d3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_READ_D and len = sizeof(uint32_t)");
+}
+
+ATF_TC_BODY(io_read_d3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint32_t lookup_me = 0;
+ const uint32_t magic = 0x1234abcd;
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = &lookup_me,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me = magic;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %" PRIx32 " != expected %" PRIx32, lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_d4);
+ATF_TC_HEAD(io_read_d4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_READ_D and len = sizeof(uint64_t)");
+}
+
+ATF_TC_BODY(io_read_d4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint64_t lookup_me = 0;
+ const uint64_t magic = 0x1234abcd9876dcfa;
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = &lookup_me,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me = magic;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %" PRIx64 " != expected %" PRIx64, lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_write_d1);
+ATF_TC_HEAD(io_write_d1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_WRITE_D and len = sizeof(uint8_t)");
+}
+
+ATF_TC_BODY(io_write_d1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint8_t lookup_me = 0;
+ const uint8_t magic = 0xab;
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_WRITE_D,
+ .piod_offs = &lookup_me,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me, magic);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ lookup_me = magic;
+
+ printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_write_d2);
+ATF_TC_HEAD(io_write_d2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_WRITE_D and len = sizeof(uint16_t)");
+}
+
+ATF_TC_BODY(io_write_d2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint16_t lookup_me = 0;
+ const uint16_t magic = 0xab12;
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_WRITE_D,
+ .piod_offs = &lookup_me,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me, magic);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ lookup_me = magic;
+
+ printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_write_d3);
+ATF_TC_HEAD(io_write_d3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_WRITE_D and len = sizeof(uint32_t)");
+}
+
+ATF_TC_BODY(io_write_d3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint32_t lookup_me = 0;
+ const uint32_t magic = 0xab127643;
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_WRITE_D,
+ .piod_offs = &lookup_me,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me, magic);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ lookup_me = magic;
+
+ printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_write_d4);
+ATF_TC_HEAD(io_write_d4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_WRITE_D and len = sizeof(uint64_t)");
+}
+
+ATF_TC_BODY(io_write_d4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint64_t lookup_me = 0;
+ const uint64_t magic = 0xab12764376490123;
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_WRITE_D,
+ .piod_offs = &lookup_me,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me, magic);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ lookup_me = magic;
+
+ printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_auxv1);
+ATF_TC_HEAD(io_read_auxv1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_AUXV called for tracee");
+}
+
+ATF_TC_BODY(io_read_auxv1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ AuxInfo ai[100], *aip;
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_READ_AUXV,
+ .piod_offs = 0,
+ .piod_addr = ai,
+ .piod_len = sizeof(ai)
+ };
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new AUXV from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ printf("Asserting that AUXV length (%zu) is > 0\n", io.piod_len);
+ ATF_REQUIRE(io.piod_len > 0);
+
+ for (aip = ai; aip->a_type != AT_NULL; aip++)
+ printf("a_type=%#llx a_v=%#llx\n",
+ (long long int)aip->a_type, (long long int)aip->a_v);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_d1);
+ATF_TC_HEAD(read_d1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_D called once");
+}
+
+ATF_TC_BODY(read_d1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me = 0;
+ const int magic = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me = magic;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me = ptrace(PT_READ_D, child, &lookup_me, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %#x != expected %#x", lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_d2);
+ATF_TC_HEAD(read_d2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_D called twice");
+}
+
+ATF_TC_BODY(read_d2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me1 = 0;
+ int lookup_me2 = 0;
+ const int magic1 = (int)random();
+ const int magic2 = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me1 = magic1;
+ lookup_me2 = magic2;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me1 = ptrace(PT_READ_D, child, &lookup_me1, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me1, magic1,
+ "got value %#x != expected %#x", lookup_me1, magic1);
+
+ printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me2 = ptrace(PT_READ_D, child, &lookup_me2, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me2, magic2,
+ "got value %#x != expected %#x", lookup_me2, magic2);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_d3);
+ATF_TC_HEAD(read_d3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_D called three times");
+}
+
+ATF_TC_BODY(read_d3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me1 = 0;
+ int lookup_me2 = 0;
+ int lookup_me3 = 0;
+ const int magic1 = (int)random();
+ const int magic2 = (int)random();
+ const int magic3 = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me1 = magic1;
+ lookup_me2 = magic2;
+ lookup_me3 = magic3;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me1 = ptrace(PT_READ_D, child, &lookup_me1, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me1, magic1,
+ "got value %#x != expected %#x", lookup_me1, magic1);
+
+ printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me2 = ptrace(PT_READ_D, child, &lookup_me2, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me2, magic2,
+ "got value %#x != expected %#x", lookup_me2, magic2);
+
+ printf("Read new lookup_me3 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me3 = ptrace(PT_READ_D, child, &lookup_me3, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me3, magic3,
+ "got value %#x != expected %#x", lookup_me3, magic3);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_d4);
+ATF_TC_HEAD(read_d4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_D called four times");
+}
+
+ATF_TC_BODY(read_d4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me1 = 0;
+ int lookup_me2 = 0;
+ int lookup_me3 = 0;
+ int lookup_me4 = 0;
+ const int magic1 = (int)random();
+ const int magic2 = (int)random();
+ const int magic3 = (int)random();
+ const int magic4 = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me1 = magic1;
+ lookup_me2 = magic2;
+ lookup_me3 = magic3;
+ lookup_me4 = magic4;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me1 = ptrace(PT_READ_D, child, &lookup_me1, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me1, magic1,
+ "got value %#x != expected %#x", lookup_me1, magic1);
+
+ printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me2 = ptrace(PT_READ_D, child, &lookup_me2, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me2, magic2,
+ "got value %#x != expected %#x", lookup_me2, magic2);
+
+ printf("Read new lookup_me3 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me3 = ptrace(PT_READ_D, child, &lookup_me3, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me3, magic3,
+ "got value %#x != expected %#x", lookup_me3, magic3);
+
+ printf("Read new lookup_me4 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me4 = ptrace(PT_READ_D, child, &lookup_me4, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me4, magic4,
+ "got value %#x != expected %#x", lookup_me4, magic4);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(write_d1);
+ATF_TC_HEAD(write_d1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_WRITE_D called once");
+}
+
+ATF_TC_BODY(write_d1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me = 0;
+ const int magic = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me, magic);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Write new lookup_me to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me, magic) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(write_d2);
+ATF_TC_HEAD(write_d2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_WRITE_D called twice");
+}
+
+ATF_TC_BODY(write_d2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me1 = 0;
+ int lookup_me2 = 0;
+ const int magic1 = (int)random();
+ const int magic2 = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me1, magic1);
+ FORKEE_ASSERT_EQ(lookup_me2, magic2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Write new lookup_me1 to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me1, magic1) != -1);
+
+ printf("Write new lookup_me2 to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me2, magic2) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(write_d3);
+ATF_TC_HEAD(write_d3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_WRITE_D called three times");
+}
+
+ATF_TC_BODY(write_d3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me1 = 0;
+ int lookup_me2 = 0;
+ int lookup_me3 = 0;
+ const int magic1 = (int)random();
+ const int magic2 = (int)random();
+ const int magic3 = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me1, magic1);
+ FORKEE_ASSERT_EQ(lookup_me2, magic2);
+ FORKEE_ASSERT_EQ(lookup_me3, magic3);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Write new lookup_me1 to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me1, magic1) != -1);
+
+ printf("Write new lookup_me2 to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me2, magic2) != -1);
+
+ printf("Write new lookup_me3 to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me3, magic3) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(write_d4);
+ATF_TC_HEAD(write_d4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_WRITE_D called four times");
+}
+
+ATF_TC_BODY(write_d4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me1 = 0;
+ int lookup_me2 = 0;
+ int lookup_me3 = 0;
+ int lookup_me4 = 0;
+ const int magic1 = (int)random();
+ const int magic2 = (int)random();
+ const int magic3 = (int)random();
+ const int magic4 = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me1, magic1);
+ FORKEE_ASSERT_EQ(lookup_me2, magic2);
+ FORKEE_ASSERT_EQ(lookup_me3, magic3);
+ FORKEE_ASSERT_EQ(lookup_me4, magic4);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Write new lookup_me1 to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me1, magic1) != -1);
+
+ printf("Write new lookup_me2 to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me2, magic2) != -1);
+
+ printf("Write new lookup_me3 to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me3, magic3) != -1);
+
+ printf("Write new lookup_me4 to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, &lookup_me4, magic4) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_d_write_d_handshake1);
+ATF_TC_HEAD(io_read_d_write_d_handshake1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_READ_D and PIOD_WRITE_D handshake");
+}
+
+ATF_TC_BODY(io_read_d_write_d_handshake1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint8_t lookup_me_fromtracee = 0;
+ const uint8_t magic_fromtracee = (uint8_t)random();
+ uint8_t lookup_me_totracee = 0;
+ const uint8_t magic_totracee = (uint8_t)random();
+ struct ptrace_io_desc io_fromtracee = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = &lookup_me_fromtracee,
+ .piod_addr = &lookup_me_fromtracee,
+ .piod_len = sizeof(lookup_me_fromtracee)
+ };
+ struct ptrace_io_desc io_totracee = {
+ .piod_op = PIOD_WRITE_D,
+ .piod_offs = &lookup_me_totracee,
+ .piod_addr = &lookup_me_totracee,
+ .piod_len = sizeof(lookup_me_totracee)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me_fromtracee = magic_fromtracee;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me_totracee, magic_totracee);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read lookup_me_fromtracee PID=%d by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io_fromtracee, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me_fromtracee, magic_fromtracee,
+ "got value %" PRIx8 " != expected %" PRIx8, lookup_me_fromtracee,
+ magic_fromtracee);
+
+ lookup_me_totracee = magic_totracee;
+
+ printf("Write lookup_me_totracee to PID=%d by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io_totracee, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me_totracee, magic_totracee,
+ "got value %" PRIx8 " != expected %" PRIx8, lookup_me_totracee,
+ magic_totracee);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_d_write_d_handshake2);
+ATF_TC_HEAD(io_read_d_write_d_handshake2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_WRITE_D and PIOD_READ_D handshake");
+}
+
+ATF_TC_BODY(io_read_d_write_d_handshake2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint8_t lookup_me_fromtracee = 0;
+ const uint8_t magic_fromtracee = (uint8_t)random();
+ uint8_t lookup_me_totracee = 0;
+ const uint8_t magic_totracee = (uint8_t)random();
+ struct ptrace_io_desc io_fromtracee = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = &lookup_me_fromtracee,
+ .piod_addr = &lookup_me_fromtracee,
+ .piod_len = sizeof(lookup_me_fromtracee)
+ };
+ struct ptrace_io_desc io_totracee = {
+ .piod_op = PIOD_WRITE_D,
+ .piod_offs = &lookup_me_totracee,
+ .piod_addr = &lookup_me_totracee,
+ .piod_len = sizeof(lookup_me_totracee)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me_fromtracee = magic_fromtracee;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me_totracee, magic_totracee);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ lookup_me_totracee = magic_totracee;
+
+ printf("Write lookup_me_totracee to PID=%d by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io_totracee, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me_totracee, magic_totracee,
+ "got value %" PRIx8 " != expected %" PRIx8, lookup_me_totracee,
+ magic_totracee);
+
+ printf("Read lookup_me_fromtracee PID=%d by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io_fromtracee, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me_fromtracee, magic_fromtracee,
+ "got value %" PRIx8 " != expected %" PRIx8, lookup_me_fromtracee,
+ magic_fromtracee);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_d_write_d_handshake1);
+ATF_TC_HEAD(read_d_write_d_handshake1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_D with PT_WRITE_D handshake");
+}
+
+ATF_TC_BODY(read_d_write_d_handshake1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me_fromtracee = 0;
+ const int magic_fromtracee = (int)random();
+ int lookup_me_totracee = 0;
+ const int magic_totracee = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me_fromtracee = magic_fromtracee;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me_totracee, magic_totracee);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new lookup_me_fromtracee PID=%d by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me_fromtracee =
+ ptrace(PT_READ_D, child, &lookup_me_fromtracee, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me_fromtracee, magic_fromtracee,
+ "got value %#x != expected %#x", lookup_me_fromtracee,
+ magic_fromtracee);
+
+ printf("Write new lookup_me_totracee to PID=%d from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE
+ (ptrace(PT_WRITE_D, child, &lookup_me_totracee, magic_totracee)
+ != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_d_write_d_handshake2);
+ATF_TC_HEAD(read_d_write_d_handshake2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_WRITE_D with PT_READ_D handshake");
+}
+
+ATF_TC_BODY(read_d_write_d_handshake2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me_fromtracee = 0;
+ const int magic_fromtracee = (int)random();
+ int lookup_me_totracee = 0;
+ const int magic_totracee = (int)random();
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ lookup_me_fromtracee = magic_fromtracee;
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(lookup_me_totracee, magic_totracee);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Write new lookup_me_totracee to PID=%d from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE
+ (ptrace(PT_WRITE_D, child, &lookup_me_totracee, magic_totracee)
+ != -1);
+
+ printf("Read new lookup_me_fromtracee PID=%d by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me_fromtracee =
+ ptrace(PT_READ_D, child, &lookup_me_fromtracee, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me_fromtracee, magic_fromtracee,
+ "got value %#x != expected %#x", lookup_me_fromtracee,
+ magic_fromtracee);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+/* These dummy functions are used to be copied with ptrace(2) calls */
+static int __used
+dummy_fn1(int a, int b, int c, int d)
+{
+
+ a *= 1;
+ b += 2;
+ c -= 3;
+ d /= 4;
+
+ return a + b * c - d;
+}
+
+static int __used
+dummy_fn2(int a, int b, int c, int d)
+{
+
+ a *= 4;
+ b += 3;
+ c -= 2;
+ d /= 1;
+
+ return a + b * c - d;
+}
+
+static int __used
+dummy_fn3(int a, int b, int c, int d)
+{
+
+ a *= 10;
+ b += 20;
+ c -= 30;
+ d /= 40;
+
+ return a + b * c - d;
+}
+
+static int __used
+dummy_fn4(int a, int b, int c, int d)
+{
+
+ a *= 40;
+ b += 30;
+ c -= 20;
+ d /= 10;
+
+ return a + b * c - d;
+}
+
+ATF_TC(io_read_i1);
+ATF_TC_HEAD(io_read_i1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_READ_I and len = sizeof(uint8_t)");
+}
+
+ATF_TC_BODY(io_read_i1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint8_t lookup_me = 0;
+ uint8_t magic;
+ memcpy(&magic, dummy_fn1, sizeof(magic));
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_READ_I,
+ .piod_offs = dummy_fn1,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %" PRIx8 " != expected %" PRIx8, lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_i2);
+ATF_TC_HEAD(io_read_i2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_READ_I and len = sizeof(uint16_t)");
+}
+
+ATF_TC_BODY(io_read_i2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint16_t lookup_me = 0;
+ uint16_t magic;
+ memcpy(&magic, dummy_fn1, sizeof(magic));
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_READ_I,
+ .piod_offs = dummy_fn1,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %" PRIx16 " != expected %" PRIx16, lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_i3);
+ATF_TC_HEAD(io_read_i3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_READ_I and len = sizeof(uint32_t)");
+}
+
+ATF_TC_BODY(io_read_i3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint32_t lookup_me = 0;
+ uint32_t magic;
+ memcpy(&magic, dummy_fn1, sizeof(magic));
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_READ_I,
+ .piod_offs = dummy_fn1,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %" PRIx32 " != expected %" PRIx32, lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(io_read_i4);
+ATF_TC_HEAD(io_read_i4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_IO with PIOD_READ_I and len = sizeof(uint64_t)");
+}
+
+ATF_TC_BODY(io_read_i4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint64_t lookup_me = 0;
+ uint64_t magic;
+ memcpy(&magic, dummy_fn1, sizeof(magic));
+ struct ptrace_io_desc io = {
+ .piod_op = PIOD_READ_I,
+ .piod_offs = dummy_fn1,
+ .piod_addr = &lookup_me,
+ .piod_len = sizeof(lookup_me)
+ };
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_IO, child, &io, 0) != -1);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %" PRIx64 " != expected %" PRIx64, lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_i1);
+ATF_TC_HEAD(read_i1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_I called once");
+}
+
+ATF_TC_BODY(read_i1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me = 0;
+ int magic;
+ memcpy(&magic, dummy_fn1, sizeof(magic));
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new lookup_me from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me = ptrace(PT_READ_I, child, dummy_fn1, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me, magic,
+ "got value %#x != expected %#x", lookup_me, magic);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_i2);
+ATF_TC_HEAD(read_i2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_I called twice");
+}
+
+ATF_TC_BODY(read_i2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me1 = 0;
+ int lookup_me2 = 0;
+ int magic1;
+ int magic2;
+ memcpy(&magic1, dummy_fn1, sizeof(magic1));
+ memcpy(&magic2, dummy_fn2, sizeof(magic2));
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me1 = ptrace(PT_READ_I, child, dummy_fn1, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me1, magic1,
+ "got value %#x != expected %#x", lookup_me1, magic1);
+
+ printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me2 = ptrace(PT_READ_I, child, dummy_fn2, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me2, magic2,
+ "got value %#x != expected %#x", lookup_me2, magic2);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_i3);
+ATF_TC_HEAD(read_i3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_I called three times");
+}
+
+ATF_TC_BODY(read_i3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me1 = 0;
+ int lookup_me2 = 0;
+ int lookup_me3 = 0;
+ int magic1;
+ int magic2;
+ int magic3;
+ memcpy(&magic1, dummy_fn1, sizeof(magic1));
+ memcpy(&magic2, dummy_fn2, sizeof(magic2));
+ memcpy(&magic3, dummy_fn3, sizeof(magic3));
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me1 = ptrace(PT_READ_I, child, dummy_fn1, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me1, magic1,
+ "got value %#x != expected %#x", lookup_me1, magic1);
+
+ printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me2 = ptrace(PT_READ_I, child, dummy_fn2, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me2, magic2,
+ "got value %#x != expected %#x", lookup_me2, magic2);
+
+ printf("Read new lookup_me3 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me3 = ptrace(PT_READ_I, child, dummy_fn3, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me3, magic3,
+ "got value %#x != expected %#x", lookup_me3, magic3);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(read_i4);
+ATF_TC_HEAD(read_i4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_READ_I called four times");
+}
+
+ATF_TC_BODY(read_i4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ int lookup_me1 = 0;
+ int lookup_me2 = 0;
+ int lookup_me3 = 0;
+ int lookup_me4 = 0;
+ int magic1;
+ int magic2;
+ int magic3;
+ int magic4;
+ memcpy(&magic1, dummy_fn1, sizeof(magic1));
+ memcpy(&magic2, dummy_fn2, sizeof(magic2));
+ memcpy(&magic3, dummy_fn3, sizeof(magic3));
+ memcpy(&magic4, dummy_fn4, sizeof(magic4));
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Read new lookup_me1 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me1 = ptrace(PT_READ_I, child, dummy_fn1, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me1, magic1,
+ "got value %#x != expected %#x", lookup_me1, magic1);
+
+ printf("Read new lookup_me2 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me2 = ptrace(PT_READ_I, child, dummy_fn2, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me2, magic2,
+ "got value %#x != expected %#x", lookup_me2, magic2);
+
+ printf("Read new lookup_me3 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me3 = ptrace(PT_READ_I, child, dummy_fn3, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me3, magic3,
+ "got value %#x != expected %#x", lookup_me3, magic3);
+
+ printf("Read new lookup_me4 from tracee (PID=%d) by tracer (PID=%d)\n",
+ child, getpid());
+ errno = 0;
+ lookup_me4 = ptrace(PT_READ_I, child, dummy_fn4, 0);
+ ATF_REQUIRE_EQ(errno, 0);
+
+ ATF_REQUIRE_EQ_MSG(lookup_me4, magic4,
+ "got value %#x != expected %#x", lookup_me4, magic4);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#if defined(HAVE_GPREGS)
+ATF_TC(regs1);
+ATF_TC_HEAD(regs1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify plain PT_GETREGS call without further steps");
+}
+
+ATF_TC_BODY(regs1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct reg r;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(HAVE_GPREGS)
+ATF_TC(regs2);
+ATF_TC_HEAD(regs2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify plain PT_GETREGS call and retrieve PC");
+}
+
+ATF_TC_BODY(regs2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct reg r;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1);
+
+ printf("Retrieved PC=%" PRIxREGISTER "\n", PTRACE_REG_PC(&r));
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(HAVE_GPREGS)
+ATF_TC(regs3);
+ATF_TC_HEAD(regs3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify plain PT_GETREGS call and retrieve SP");
+}
+
+ATF_TC_BODY(regs3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct reg r;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1);
+
+ printf("Retrieved SP=%" PRIxREGISTER "\n", PTRACE_REG_SP(&r));
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(HAVE_GPREGS)
+ATF_TC(regs4);
+ATF_TC_HEAD(regs4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify plain PT_GETREGS call and retrieve INTRV");
+}
+
+ATF_TC_BODY(regs4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct reg r;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1);
+
+ printf("Retrieved INTRV=%" PRIxREGISTER "\n", PTRACE_REG_INTRV(&r));
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(HAVE_GPREGS)
+ATF_TC(regs5);
+ATF_TC_HEAD(regs5, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_GETREGS and PT_SETREGS calls without changing regs");
+}
+
+ATF_TC_BODY(regs5, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct reg r;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1);
+
+ printf("Call SETREGS for the child process (without changed regs)\n");
+ ATF_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(HAVE_FPREGS)
+ATF_TC(fpregs1);
+ATF_TC_HEAD(fpregs1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify plain PT_GETFPREGS call without further steps");
+}
+
+ATF_TC_BODY(fpregs1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct fpreg r;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETFPREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETFPREGS, child, &r, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(HAVE_FPREGS)
+ATF_TC(fpregs2);
+ATF_TC_HEAD(fpregs2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_GETFPREGS and PT_SETFPREGS calls without changing "
+ "regs");
+}
+
+ATF_TC_BODY(fpregs2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct fpreg r;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETFPREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETFPREGS, child, &r, 0) != -1);
+
+ printf("Call SETFPREGS for the child (without changed regs)\n");
+ ATF_REQUIRE(ptrace(PT_SETFPREGS, child, &r, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(PT_STEP)
+static void
+ptrace_step(int N)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ int happy;
+
+#if defined(__arm__)
+ /* PT_STEP not supported on arm 32-bit */
+ atf_tc_expect_fail("PR kern/52119");
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ happy = check_happy(999);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(happy, check_happy(999));
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ while (N --> 0) {
+ printf("Before resuming the child process where it left off "
+ "and without signal to be sent (use PT_STEP)\n");
+ ATF_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0),
+ child);
+
+ validate_status_stopped(status, SIGTRAP);
+ }
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(PT_STEP)
+ATF_TC(step1);
+ATF_TC_HEAD(step1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify single PT_STEP call");
+}
+
+ATF_TC_BODY(step1, tc)
+{
+ ptrace_step(1);
+}
+#endif
+
+#if defined(PT_STEP)
+ATF_TC(step2);
+ATF_TC_HEAD(step2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_STEP called twice");
+}
+
+ATF_TC_BODY(step2, tc)
+{
+ ptrace_step(2);
+}
+#endif
+
+#if defined(PT_STEP)
+ATF_TC(step3);
+ATF_TC_HEAD(step3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_STEP called three times");
+}
+
+ATF_TC_BODY(step3, tc)
+{
+ ptrace_step(3);
+}
+#endif
+
+#if defined(PT_STEP)
+ATF_TC(step4);
+ATF_TC_HEAD(step4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify PT_STEP called four times");
+}
+
+ATF_TC_BODY(step4, tc)
+{
+ ptrace_step(3);
+}
+#endif
+
+ATF_TC(kill1);
+ATF_TC_HEAD(kill1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that PT_CONTINUE with SIGKILL terminates child");
+}
+
+ATF_TC_BODY(kill1, tc)
+{
+ const int sigval = SIGSTOP, sigsent = SIGKILL;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ /* NOTREACHED */
+ FORKEE_ASSERTX(0 &&
+ "Child should be terminated by a signal from its parent");
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigsent) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_signaled(status, sigsent, 0);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(kill2);
+ATF_TC_HEAD(kill2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that PT_KILL terminates child");
+}
+
+ATF_TC_BODY(kill2, tc)
+{
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ /* NOTREACHED */
+ FORKEE_ASSERTX(0 &&
+ "Child should be terminated by a signal from its parent");
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_KILL, child, (void*)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_signaled(status, SIGKILL, 0);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(lwpinfo1);
+ATF_TC_HEAD(lwpinfo1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify basic LWPINFO call for single thread (PT_TRACE_ME)");
+}
+
+ATF_TC_BODY(lwpinfo1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct ptrace_lwpinfo info = {0, 0};
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling ptrace(2) with PT_LWPINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_LWPINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Assert that there exists a thread\n");
+ ATF_REQUIRE(info.pl_lwpid > 0);
+
+ printf("Assert that lwp thread %d received event PL_EVENT_SIGNAL\n",
+ info.pl_lwpid);
+ ATF_REQUIRE_EQ_MSG(info.pl_event, PL_EVENT_SIGNAL,
+ "Received event %d != expected event %d",
+ info.pl_event, PL_EVENT_SIGNAL);
+
+ printf("Before calling ptrace(2) with PT_LWPINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_LWPINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Assert that there are no more lwp threads in child\n");
+ ATF_REQUIRE_EQ(info.pl_lwpid, 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(lwpinfo2);
+ATF_TC_HEAD(lwpinfo2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify basic LWPINFO call for single thread (PT_ATTACH from "
+ "tracer)");
+}
+
+ATF_TC_BODY(lwpinfo2, tc)
+{
+ struct msg_fds parent_tracee, parent_tracer;
+ const int exitval_tracee = 5;
+ const int exitval_tracer = 10;
+ pid_t tracee, tracer, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct ptrace_lwpinfo info = {0, 0};
+
+ printf("Spawn tracee\n");
+ ATF_REQUIRE(msg_open(&parent_tracee) == 0);
+ ATF_REQUIRE(msg_open(&parent_tracer) == 0);
+ tracee = atf_utils_fork();
+ if (tracee == 0) {
+
+ /* Wait for message from the parent */
+ CHILD_TO_PARENT("tracee ready", parent_tracee, msg);
+ CHILD_FROM_PARENT("tracee exit", parent_tracee, msg);
+
+ _exit(exitval_tracee);
+ }
+ PARENT_FROM_CHILD("tracee ready", parent_tracee, msg);
+
+ printf("Spawn debugger\n");
+ tracer = atf_utils_fork();
+ if (tracer == 0) {
+ /* No IPC to communicate with the child */
+ printf("Before calling PT_ATTACH from tracee %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_ATTACH, tracee, NULL, 0) != -1);
+
+ /* Wait for tracee and assert that it was stopped w/ SIGSTOP */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_stopped(status, SIGSTOP);
+
+ printf("Before calling ptrace(2) with PT_LWPINFO for child\n");
+ FORKEE_ASSERT(ptrace(PT_LWPINFO, tracee, &info, sizeof(info))
+ != -1);
+
+ printf("Assert that there exists a thread\n");
+ FORKEE_ASSERTX(info.pl_lwpid > 0);
+
+ printf("Assert that lwp thread %d received event "
+ "PL_EVENT_SIGNAL\n", info.pl_lwpid);
+ FORKEE_ASSERT_EQ(info.pl_event, PL_EVENT_SIGNAL);
+
+ printf("Before calling ptrace(2) with PT_LWPINFO for child\n");
+ FORKEE_ASSERT(ptrace(PT_LWPINFO, tracee, &info, sizeof(info))
+ != -1);
+
+ printf("Assert that there are no more lwp threads in child\n");
+ FORKEE_ASSERTX(info.pl_lwpid == 0);
+
+ /* Resume tracee with PT_CONTINUE */
+ FORKEE_ASSERT(ptrace(PT_CONTINUE, tracee, (void *)1, 0) != -1);
+
+ /* Inform parent that tracer has attached to tracee */
+ CHILD_TO_PARENT("tracer ready", parent_tracer, msg);
+ /* Wait for parent */
+ CHILD_FROM_PARENT("tracer wait", parent_tracer, msg);
+
+ /* Wait for tracee and assert that it exited */
+ FORKEE_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, 0), tracee);
+
+ forkee_status_exited(status, exitval_tracee);
+
+ printf("Before exiting of the tracer process\n");
+ _exit(exitval_tracer);
+ }
+
+ printf("Wait for the tracer to attach to the tracee\n");
+ PARENT_FROM_CHILD("tracer ready", parent_tracer, msg);
+
+ printf("Resume the tracee and let it exit\n");
+ PARENT_TO_CHILD("tracee exit", parent_tracee, msg);
+
+ printf("Detect that tracee is zombie\n");
+ await_zombie(tracee);
+
+ printf("Assert that there is no status about tracee - "
+ "Tracer must detect zombie first - calling %s()\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(
+ wpid = TWAIT_GENERIC(tracee, &status, WNOHANG), 0);
+
+ printf("Resume the tracer and let it detect exited tracee\n");
+ PARENT_TO_CHILD("tracer wait", parent_tracer, msg);
+
+ printf("Wait for tracer to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracer, &status, 0),
+ tracer);
+
+ validate_status_exited(status, exitval_tracer);
+
+ printf("Wait for tracee to finish its job and exit - calling %s()\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(tracee, &status, WNOHANG),
+ tracee);
+
+ validate_status_exited(status, exitval_tracee);
+
+ msg_close(&parent_tracer);
+ msg_close(&parent_tracee);
+}
+#endif
+
+ATF_TC(siginfo1);
+ATF_TC_HEAD(siginfo1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify basic PT_GET_SIGINFO call for SIGTRAP from tracee");
+}
+
+ATF_TC_BODY(siginfo1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(siginfo2);
+ATF_TC_HEAD(siginfo2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify basic PT_GET_SIGINFO and PT_SET_SIGINFO calls without "
+ "modification of SIGINT from tracee");
+}
+
+static int siginfo2_caught = 0;
+
+static void
+siginfo2_sighandler(int sig)
+{
+ FORKEE_ASSERT_EQ(sig, SIGINT);
+
+ ++siginfo2_caught;
+}
+
+ATF_TC_BODY(siginfo2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGINT;
+ pid_t child, wpid;
+ struct sigaction sa;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sa.sa_handler = siginfo2_sighandler;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+
+ FORKEE_ASSERT(sigaction(sigval, &sa, NULL) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(siginfo2_caught, 1);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ printf("Before calling ptrace(2) with PT_SET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_SET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigval) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(siginfo3);
+ATF_TC_HEAD(siginfo3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify basic PT_GET_SIGINFO and PT_SET_SIGINFO calls with "
+ "setting signal to new value");
+}
+
+static int siginfo3_caught = 0;
+
+static void
+siginfo3_sigaction(int sig, siginfo_t *info, void *ctx)
+{
+ FORKEE_ASSERT_EQ(sig, SIGTRAP);
+
+ FORKEE_ASSERT_EQ(info->si_signo, SIGTRAP);
+ FORKEE_ASSERT_EQ(info->si_code, TRAP_BRKPT);
+
+ ++siginfo3_caught;
+}
+
+ATF_TC_BODY(siginfo3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGINT;
+ const int sigfaked = SIGTRAP;
+ const int sicodefaked = TRAP_BRKPT;
+ pid_t child, wpid;
+ struct sigaction sa;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sa.sa_sigaction = siginfo3_sigaction;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+
+ FORKEE_ASSERT(sigaction(sigfaked, &sa, NULL) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(siginfo3_caught, 1);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ printf("Before setting new faked signal to signo=%d si_code=%d\n",
+ sigfaked, sicodefaked);
+ info.psi_siginfo.si_signo = sigfaked;
+ info.psi_siginfo.si_code = sicodefaked;
+
+ printf("Before calling ptrace(2) with PT_SET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_SET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigfaked);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, sicodefaked);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, sigfaked) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(siginfo4);
+ATF_TC_HEAD(siginfo4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Detect SIGTRAP TRAP_EXEC from tracee");
+}
+
+ATF_TC_BODY(siginfo4, tc)
+{
+ const int sigval = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before calling execve(2) from child\n");
+ execlp("/bin/echo", "/bin/echo", NULL);
+
+ FORKEE_ASSERT(0 && "Not reached");
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_EXEC);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(siginfo5);
+ATF_TC_HEAD(siginfo5, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that fork(2) is intercepted by ptrace(2) with EVENT_MASK "
+ "set to PTRACE_FORK and reports correct signal information");
+}
+
+ATF_TC_BODY(siginfo5, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+ struct ptrace_siginfo info;
+
+#if defined(__sparc__) || defined(__alpha__) || defined(__arm__)
+ /* ptrace(2) PTRACE_FORK fails on some platforms */
+ atf_tc_expect_fail("PR kern/52117");
+#endif
+
+ memset(&info, 0, sizeof(info));
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = fork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP);
+
+ printf("Enable PTRACE_FORK in EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_FORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ printf("We expect two SIGTRAP events, for child %d (TRAP_CHLD, "
+ "pe_report_event=PTRACE_FORK, state.pe_other_pid=child2) and "
+ "for child2 (TRAP_CHLD, pe_report_event=PTRACE_FORK, "
+ "state.pe_other_pid=child)\n", child);
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child %d\n", TWAIT_FNAME, child);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_CHLD);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK);
+
+ child2 = state.pe_other_pid;
+ printf("Reported PTRACE_FORK event with forkee %d\n", child2);
+
+ printf("Before calling %s() for the forkee %d of the child %d\n",
+ TWAIT_FNAME, child2, child);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_CHLD);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK);
+ ATF_REQUIRE_EQ(state.pe_other_pid, child);
+
+ printf("Before resuming the forkee process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the forkee - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_exited(status, exitval2);
+
+ printf("Before calling %s() for the forkee - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD,
+ wpid = TWAIT_GENERIC(child2, &status, 0));
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGCHLD);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, CLD_EXITED);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(PT_STEP)
+ATF_TC(siginfo6);
+ATF_TC_HEAD(siginfo6, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify single PT_STEP call with signal information check");
+}
+
+ATF_TC_BODY(siginfo6, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ int happy;
+ struct ptrace_siginfo info;
+
+#if defined(__arm__)
+ /* PT_STEP not supported on arm 32-bit */
+ atf_tc_expect_fail("PR kern/52119");
+#endif
+
+ memset(&info, 0, sizeof(info));
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ happy = check_happy(100);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(happy, check_happy(100));
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, SI_LWP);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent (use PT_STEP)\n");
+ ATF_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_TRACE);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+volatile lwpid_t the_lwp_id = 0;
+
+static void
+lwp_main_func(void *arg)
+{
+ the_lwp_id = _lwp_self();
+ _lwp_exit();
+}
+
+ATF_TC(lwp_create1);
+ATF_TC_HEAD(lwp_create1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that 1 LWP creation is intercepted by ptrace(2) with "
+ "EVENT_MASK set to PTRACE_LWP_CREATE");
+}
+
+ATF_TC_BODY(lwp_create1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+ ucontext_t uc;
+ lwpid_t lid;
+ static const size_t ssize = 16*1024;
+ void *stack;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before allocating memory for stack in child\n");
+ FORKEE_ASSERT((stack = malloc(ssize)) != NULL);
+
+ printf("Before making context for new lwp in child\n");
+ _lwp_makecontext(&uc, lwp_main_func, NULL, NULL, stack, ssize);
+
+ printf("Before creating new in child\n");
+ FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0);
+
+ printf("Before waiting for lwp %d to exit\n", lid);
+ FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0);
+
+ printf("Before verifying that reported %d and running lid %d "
+ "are the same\n", lid, the_lwp_id);
+ FORKEE_ASSERT_EQ(lid, the_lwp_id);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Set empty EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_LWP_CREATE;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGTRAP\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_CREATE);
+
+ lid = state.pe_lwp;
+ printf("Reported PTRACE_LWP_CREATE event with lid %d\n", lid);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(lwp_exit1);
+ATF_TC_HEAD(lwp_exit1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that 1 LWP creation is intercepted by ptrace(2) with "
+ "EVENT_MASK set to PTRACE_LWP_EXIT");
+}
+
+ATF_TC_BODY(lwp_exit1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+ ucontext_t uc;
+ lwpid_t lid;
+ static const size_t ssize = 16*1024;
+ void *stack;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before allocating memory for stack in child\n");
+ FORKEE_ASSERT((stack = malloc(ssize)) != NULL);
+
+ printf("Before making context for new lwp in child\n");
+ _lwp_makecontext(&uc, lwp_main_func, NULL, NULL, stack, ssize);
+
+ printf("Before creating new in child\n");
+ FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0);
+
+ printf("Before waiting for lwp %d to exit\n", lid);
+ FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0);
+
+ printf("Before verifying that reported %d and running lid %d "
+ "are the same\n", lid, the_lwp_id);
+ FORKEE_ASSERT_EQ(lid, the_lwp_id);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Set empty EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_LWP_EXIT;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGTRAP\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_EXIT);
+
+ lid = state.pe_lwp;
+ printf("Reported PTRACE_LWP_EXIT event with lid %d\n", lid);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(signal1);
+ATF_TC_HEAD(signal1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking single unrelated signal does not stop tracer "
+ "from catching other signals");
+}
+
+ATF_TC_BODY(signal1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ const int signotmasked = SIGINT;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before raising %s from child\n",
+ strsignal(signotmasked));
+ FORKEE_ASSERT(raise(signotmasked) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, signotmasked);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(signal2);
+ATF_TC_HEAD(signal2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking SIGTRAP in tracee stops tracer from "
+ "catching this raised signal");
+}
+
+ATF_TC_BODY(signal2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before raising %s breakpoint from child\n",
+ strsignal(sigmasked));
+ FORKEE_ASSERT(raise(sigmasked) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(signal3);
+ATF_TC_HEAD(signal3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking SIGTRAP in tracee does not stop tracer from "
+ "catching software breakpoints");
+}
+
+ATF_TC_BODY(signal3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+
+ atf_tc_expect_fail("PR kern/51918");
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before raising software breakpoint from child\n");
+#if defined(__x86_64__)
+ __asm__ __volatile__ ("int3\n;");
+#else
+ /* port me */
+#endif
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigmasked);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#if defined(PT_STEP)
+ATF_TC(signal4);
+ATF_TC_HEAD(signal4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking SIGTRAP in tracee does not stop tracer from "
+ "catching single step trap");
+}
+
+ATF_TC_BODY(signal4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+ int happy;
+
+#if defined(__arm__)
+ /* PT_STEP not supported on arm 32-bit */
+ atf_tc_expect_fail("PR kern/51918 PR kern/52119");
+#else
+ atf_tc_expect_fail("PR kern/52119");
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ happy = check_happy(100);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT_EQ(happy, check_happy(100));
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_STEP, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigmasked);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+ATF_TC(signal5);
+ATF_TC_HEAD(signal5, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking SIGTRAP in tracee does not stop tracer from "
+ "catching exec() breakpoint");
+}
+
+ATF_TC_BODY(signal5, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+
+ atf_tc_expect_fail("PR kern/51918");
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before calling execve(2) from child\n");
+ execlp("/bin/echo", "/bin/echo", NULL);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigmasked);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(signal6);
+ATF_TC_HEAD(signal6, tc)
+{
+ atf_tc_set_md_var(tc, "timeout", "5");
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking SIGTRAP in tracee does not stop tracer from "
+ "catching PTRACE_FORK breakpoint");
+}
+
+ATF_TC_BODY(signal6, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+
+ atf_tc_expect_timeout("PR kern/51918");
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = fork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Enable PTRACE_FORK in EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_FORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigmasked);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK);
+
+ child2 = state.pe_other_pid;
+ printf("Reported PTRACE_FORK event with forkee %d\n", child2);
+
+ printf("Before calling %s() for the child2\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_FORK);
+ ATF_REQUIRE_EQ(state.pe_other_pid, child);
+
+ printf("Before resuming the forkee process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the forkee - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_exited(status, exitval2);
+
+ printf("Before calling %s() for the forkee - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD,
+ wpid = TWAIT_GENERIC(child2, &status, 0));
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(TWAIT_HAVE_PID)
+ATF_TC(signal7);
+ATF_TC_HEAD(signal7, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking SIGTRAP in tracee does not stop tracer from "
+ "catching PTRACE_VFORK breakpoint");
+}
+
+ATF_TC_BODY(signal7, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+
+ atf_tc_expect_fail("PR kern/51918 PR kern/51630");
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = fork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Enable PTRACE_VFORK in EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_VFORK;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigmasked);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK);
+
+ child2 = state.pe_other_pid;
+ printf("Reported PTRACE_VFORK event with forkee %d\n", child2);
+
+ printf("Before calling %s() for the child2\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child2, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK);
+ ATF_REQUIRE_EQ(state.pe_other_pid, child);
+
+ printf("Before resuming the forkee process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child2, (void *)1, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the forkee - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child2, &status, 0),
+ child2);
+
+ validate_status_exited(status, exitval2);
+
+ printf("Before calling %s() for the forkee - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD,
+ wpid = TWAIT_GENERIC(child2, &status, 0));
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+ATF_TC(signal8);
+ATF_TC_HEAD(signal8, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking SIGTRAP in tracee does not stop tracer from "
+ "catching PTRACE_VFORK_DONE breakpoint");
+}
+
+ATF_TC_BODY(signal8, tc)
+{
+ const int exitval = 5;
+ const int exitval2 = 15;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, child2, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+
+ atf_tc_expect_fail("PR kern/51918");
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ FORKEE_ASSERT((child2 = vfork()) != -1);
+
+ if (child2 == 0)
+ _exit(exitval2);
+
+ FORKEE_REQUIRE_SUCCESS
+ (wpid = TWAIT_GENERIC(child2, &status, 0), child2);
+
+ forkee_status_exited(status, exitval2);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Enable PTRACE_VFORK_DONE in EVENT_MASK for the child %d\n",
+ child);
+ event.pe_set_event = PTRACE_VFORK_DONE;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigmasked);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_VFORK_DONE);
+
+ child2 = state.pe_other_pid;
+ printf("Reported PTRACE_VFORK_DONE event with forkee %d\n", child2);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGCHLD\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGCHLD);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(signal9);
+ATF_TC_HEAD(signal9, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking SIGTRAP in tracee does not stop tracer from "
+ "catching PTRACE_LWP_CREATE breakpoint");
+}
+
+ATF_TC_BODY(signal9, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+ ucontext_t uc;
+ lwpid_t lid;
+ static const size_t ssize = 16*1024;
+ void *stack;
+
+ atf_tc_expect_fail("PR kern/51918");
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before allocating memory for stack in child\n");
+ FORKEE_ASSERT((stack = malloc(ssize)) != NULL);
+
+ printf("Before making context for new lwp in child\n");
+ _lwp_makecontext(&uc, lwp_main_func, NULL, NULL, stack, ssize);
+
+ printf("Before creating new in child\n");
+ FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0);
+
+ printf("Before waiting for lwp %d to exit\n", lid);
+ FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0);
+
+ printf("Before verifying that reported %d and running lid %d "
+ "are the same\n", lid, the_lwp_id);
+ FORKEE_ASSERT_EQ(lid, the_lwp_id);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Set empty EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_LWP_CREATE;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGTRAP\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigmasked);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_CREATE);
+
+ lid = state.pe_lwp;
+ printf("Reported PTRACE_LWP_CREATE event with lid %d\n", lid);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(signal10);
+ATF_TC_HEAD(signal10, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that masking SIGTRAP in tracee does not stop tracer from "
+ "catching PTRACE_LWP_EXIT breakpoint");
+}
+
+ATF_TC_BODY(signal10, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t intmask;
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+ ucontext_t uc;
+ lwpid_t lid;
+ static const size_t ssize = 16*1024;
+ void *stack;
+
+ atf_tc_expect_fail("PR kern/51918");
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigemptyset(&intmask);
+ sigaddset(&intmask, sigmasked);
+ sigprocmask(SIG_BLOCK, &intmask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before allocating memory for stack in child\n");
+ FORKEE_ASSERT((stack = malloc(ssize)) != NULL);
+
+ printf("Before making context for new lwp in child\n");
+ _lwp_makecontext(&uc, lwp_main_func, NULL, NULL, stack, ssize);
+
+ printf("Before creating new in child\n");
+ FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0);
+
+ printf("Before waiting for lwp %d to exit\n", lid);
+ FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0);
+
+ printf("Before verifying that reported %d and running lid %d "
+ "are the same\n", lid, the_lwp_id);
+ FORKEE_ASSERT_EQ(lid, the_lwp_id);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Set empty EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_LWP_EXIT;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGTRAP\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigmasked);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_EXIT);
+
+ lid = state.pe_lwp;
+ printf("Reported PTRACE_LWP_EXIT event with lid %d\n", lid);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(getsigmask1);
+ATF_TC_HEAD(getsigmask1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that plain PT_SET_SIGMASK can be called");
+}
+
+ATF_TC_BODY(getsigmask1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t mask;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling PT_GET_SIGMASK\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &mask, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(getsigmask2);
+ATF_TC_HEAD(getsigmask2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that PT_SET_SIGMASK reports correct mask from tracee");
+}
+
+ATF_TC_BODY(getsigmask2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ const int sigmasked = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t mask;
+ sigset_t expected_mask;
+ ATF_REQUIRE(sigemptyset(&mask) == 0);
+ ATF_REQUIRE(sigemptyset(&expected_mask) == 0);
+ ATF_REQUIRE(sigaddset(&expected_mask, sigmasked) == 0);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ sigaddset(&mask, sigmasked);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling PT_GET_SIGMASK\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &mask, 0) != -1);
+
+ ATF_REQUIRE(memcmp(&mask, &expected_mask, sizeof(sigset_t)) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(setsigmask1);
+ATF_TC_HEAD(setsigmask1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that plain PT_SET_SIGMASK can be called with empty mask");
+}
+
+ATF_TC_BODY(setsigmask1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t mask;
+ ATF_REQUIRE(sigemptyset(&mask) == 0);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling PT_SET_SIGMASK for empty mask\n");
+ ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &mask, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(setsigmask2);
+ATF_TC_HEAD(setsigmask2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that sigmask is preserved between PT_GET_SIGMASK and "
+ "PT_SET_SIGMASK");
+}
+
+ATF_TC_BODY(setsigmask2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t new_mask;
+ sigset_t mask;
+ ATF_REQUIRE(sigemptyset(&new_mask) == 0);
+ ATF_REQUIRE(sigemptyset(&mask) == 0);
+ ATF_REQUIRE(sigaddset(&mask, SIGINT) == 0);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling PT_SET_SIGMASK for new mask with SIGINT\n");
+ ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &mask, 0) != -1);
+
+ printf("Before calling PT_GET_SIGMASK to store it in new_mask\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &new_mask, 0) != -1);
+
+ ATF_REQUIRE(memcmp(&mask, &new_mask, sizeof(sigset_t)) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(setsigmask3);
+ATF_TC_HEAD(setsigmask3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that sigmask is preserved between PT_GET_SIGMASK, process "
+ "resumed and PT_SET_SIGMASK");
+}
+
+ATF_TC_BODY(setsigmask3, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t new_mask;
+ sigset_t mask;
+ ATF_REQUIRE(sigemptyset(&new_mask) == 0);
+ ATF_REQUIRE(sigemptyset(&mask) == 0);
+ ATF_REQUIRE(sigaddset(&mask, SIGINT) == 0);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling PT_SET_SIGMASK for new mask with SIGINT\n");
+ ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &mask, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling PT_GET_SIGMASK to store it in new_mask\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &new_mask, 0) != -1);
+
+ ATF_REQUIRE(memcmp(&mask, &new_mask, sizeof(sigset_t)) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(setsigmask4);
+ATF_TC_HEAD(setsigmask4, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that new sigmask is visible in tracee");
+}
+
+ATF_TC_BODY(setsigmask4, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t mask;
+ sigset_t expected_mask;
+ ATF_REQUIRE(sigemptyset(&mask) == 0);
+ ATF_REQUIRE(sigemptyset(&expected_mask) == 0);
+ ATF_REQUIRE(sigaddset(&expected_mask, SIGINT) == 0);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ sigprocmask(0, NULL, &mask);
+
+ FORKEE_ASSERT
+ (memcmp(&mask, &expected_mask, sizeof(sigset_t)) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling PT_SET_SIGMASK for new mask with SIGINT\n");
+ ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &expected_mask, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(setsigmask5);
+ATF_TC_HEAD(setsigmask5, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that sigmask cannot be set to SIGKILL");
+}
+
+ATF_TC_BODY(setsigmask5, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t new_mask;
+ sigset_t mask;
+ ATF_REQUIRE(sigemptyset(&new_mask) == 0);
+ ATF_REQUIRE(sigemptyset(&mask) == 0);
+ ATF_REQUIRE(sigaddset(&mask, SIGKILL) == 0);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling PT_SET_SIGMASK for new mask with SIGINT\n");
+ ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &mask, 0) != -1);
+
+ printf("Before calling PT_GET_SIGMASK to store it in new_mask\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &new_mask, 0) != -1);
+
+ ATF_REQUIRE(memcmp(&mask, &new_mask, sizeof(sigset_t)) != 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(setsigmask6);
+ATF_TC_HEAD(setsigmask6, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that sigmask cannot be set to SIGSTOP");
+}
+
+ATF_TC_BODY(setsigmask6, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ sigset_t new_mask;
+ sigset_t mask;
+ ATF_REQUIRE(sigemptyset(&new_mask) == 0);
+ ATF_REQUIRE(sigemptyset(&mask) == 0);
+ ATF_REQUIRE(sigaddset(&mask, SIGSTOP) == 0);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling PT_SET_SIGMASK for new mask with SIGINT\n");
+ ATF_REQUIRE(ptrace(PT_SET_SIGMASK, child, &mask, 0) != -1);
+
+ printf("Before calling PT_GET_SIGMASK to store it in new_mask\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGMASK, child, &new_mask, 0) != -1);
+
+ ATF_REQUIRE(memcmp(&mask, &new_mask, sizeof(sigset_t)) != 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+static void
+lwp_main_stop(void *arg)
+{
+ the_lwp_id = _lwp_self();
+
+ raise(SIGTRAP);
+
+ _lwp_exit();
+}
+
+ATF_TC(suspend1);
+ATF_TC_HEAD(suspend1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that a thread can be suspended by a debugger and later "
+ "resumed by a tracee");
+}
+
+ATF_TC_BODY(suspend1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ucontext_t uc;
+ lwpid_t lid;
+ static const size_t ssize = 16*1024;
+ void *stack;
+ struct ptrace_lwpinfo pl;
+ struct ptrace_siginfo psi;
+ volatile int go = 0;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before allocating memory for stack in child\n");
+ FORKEE_ASSERT((stack = malloc(ssize)) != NULL);
+
+ printf("Before making context for new lwp in child\n");
+ _lwp_makecontext(&uc, lwp_main_stop, NULL, NULL, stack, ssize);
+
+ printf("Before creating new in child\n");
+ FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0);
+
+ while (go == 0)
+ continue;
+
+ raise(SIGINT);
+
+ FORKEE_ASSERT(_lwp_continue(lid) == 0);
+
+ printf("Before waiting for lwp %d to exit\n", lid);
+ FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0);
+
+ printf("Before verifying that reported %d and running lid %d "
+ "are the same\n", lid, the_lwp_id);
+ FORKEE_ASSERT_EQ(lid, the_lwp_id);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGTRAP\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before reading siginfo and lwpid_t\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1);
+
+ printf("Before suspending LWP %d\n", psi.psi_lwpid);
+ ATF_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1);
+
+ printf("Write new go to tracee (PID=%d) from tracer (PID=%d)\n",
+ child, getpid());
+ ATF_REQUIRE(ptrace(PT_WRITE_D, child, __UNVOLATILE(&go), 1) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGINT\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGINT);
+
+ pl.pl_lwpid = 0;
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, child, &pl, sizeof(pl)) != -1);
+ while (pl.pl_lwpid != 0) {
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, child, &pl, sizeof(pl)) != -1);
+ switch (pl.pl_lwpid) {
+ case 1:
+ ATF_REQUIRE_EQ(pl.pl_event, PL_EVENT_SIGNAL);
+ break;
+ case 2:
+ ATF_REQUIRE_EQ(pl.pl_event, PL_EVENT_SUSPENDED);
+ break;
+ }
+ }
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(suspend2);
+ATF_TC_HEAD(suspend2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that the while the only thread within a process is "
+ "suspended, the whole process cannot be unstopped");
+}
+
+ATF_TC_BODY(suspend2, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct ptrace_siginfo psi;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before reading siginfo and lwpid_t\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1);
+
+ printf("Before suspending LWP %d\n", psi.psi_lwpid);
+ ATF_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE_ERRNO(EDEADLK,
+ ptrace(PT_CONTINUE, child, (void *)1, 0) == -1);
+
+ printf("Before resuming LWP %d\n", psi.psi_lwpid);
+ ATF_REQUIRE(ptrace(PT_RESUME, child, NULL, psi.psi_lwpid) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(resume1);
+ATF_TC_HEAD(resume1, tc)
+{
+ atf_tc_set_md_var(tc, "timeout", "5");
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that a thread can be suspended by a debugger and later "
+ "resumed by the debugger");
+}
+
+ATF_TC_BODY(resume1, tc)
+{
+ struct msg_fds fds;
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+ uint8_t msg = 0xde; /* dummy message for IPC based on pipe(2) */
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ucontext_t uc;
+ lwpid_t lid;
+ static const size_t ssize = 16*1024;
+ void *stack;
+ struct ptrace_lwpinfo pl;
+ struct ptrace_siginfo psi;
+
+ atf_tc_expect_timeout("PR kern/51995");
+
+ ATF_REQUIRE(msg_open(&fds) == 0);
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before allocating memory for stack in child\n");
+ FORKEE_ASSERT((stack = malloc(ssize)) != NULL);
+
+ printf("Before making context for new lwp in child\n");
+ _lwp_makecontext(&uc, lwp_main_stop, NULL, NULL, stack, ssize);
+
+ printf("Before creating new in child\n");
+ FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0);
+
+ CHILD_TO_PARENT("Message", fds, msg);
+
+ raise(SIGINT);
+
+ printf("Before waiting for lwp %d to exit\n", lid);
+ FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0);
+
+ printf("Before verifying that reported %d and running lid %d "
+ "are the same\n", lid, the_lwp_id);
+ FORKEE_ASSERT_EQ(lid, the_lwp_id);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGTRAP\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before reading siginfo and lwpid_t\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &psi, sizeof(psi)) != -1);
+
+ printf("Before suspending LWP %d\n", psi.psi_lwpid);
+ ATF_REQUIRE(ptrace(PT_SUSPEND, child, NULL, psi.psi_lwpid) != -1);
+
+ PARENT_FROM_CHILD("Message", fds, msg);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGINT\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGINT);
+
+ pl.pl_lwpid = 0;
+
+ ATF_REQUIRE(ptrace(PT_LWPINFO, child, &pl, sizeof(pl)) != -1);
+ while (pl.pl_lwpid != 0) {
+ ATF_REQUIRE(ptrace(PT_LWPINFO, child, &pl, sizeof(pl)) != -1);
+ switch (pl.pl_lwpid) {
+ case 1:
+ ATF_REQUIRE_EQ(pl.pl_event, PL_EVENT_SIGNAL);
+ break;
+ case 2:
+ ATF_REQUIRE_EQ(pl.pl_event, PL_EVENT_SUSPENDED);
+ break;
+ }
+ }
+
+ printf("Before resuming LWP %d\n", psi.psi_lwpid);
+ ATF_REQUIRE(ptrace(PT_RESUME, child, NULL, psi.psi_lwpid) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+
+ msg_close(&fds);
+
+ printf("XXX: Test worked this time but for consistency timeout it\n");
+ sleep(10);
+}
+
+ATF_TC(syscall1);
+ATF_TC_HEAD(syscall1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that getpid(2) can be traced with PT_SYSCALL");
+}
+
+ATF_TC_BODY(syscall1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ syscall(SYS_getpid);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_SYSCALL, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_SCE);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_SYSCALL, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_SCX);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(syscallemu1);
+ATF_TC_HEAD(syscallemu1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that exit(2) can be intercepted with PT_SYSCALLEMU");
+}
+
+ATF_TC_BODY(syscallemu1, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ syscall(SYS_exit, 100);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_SYSCALL, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Set SYSCALLEMU for intercepted syscall\n");
+ ATF_REQUIRE(ptrace(PT_SYSCALLEMU, child, (void *)1, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_SYSCALL, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+#include "t_ptrace_amd64_wait.h"
+#include "t_ptrace_i386_wait.h"
+#include "t_ptrace_x86_wait.h"
+
+ATF_TP_ADD_TCS(tp)
+{
+ setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stderr, NULL, _IONBF, 0);
+ ATF_TP_ADD_TC(tp, traceme1);
+ ATF_TP_ADD_TC(tp, traceme2);
+ ATF_TP_ADD_TC(tp, traceme3);
+ ATF_TP_ADD_TC(tp, traceme4);
+
+ ATF_TP_ADD_TC_HAVE_PID(tp, attach1);
+ ATF_TP_ADD_TC_HAVE_PID(tp, attach2);
+ ATF_TP_ADD_TC(tp, attach3);
+ ATF_TP_ADD_TC(tp, attach4);
+ ATF_TP_ADD_TC_HAVE_PID(tp, attach5);
+ ATF_TP_ADD_TC_HAVE_PID(tp, attach6);
+ ATF_TP_ADD_TC_HAVE_PID(tp, attach7);
+
+ ATF_TP_ADD_TC(tp, eventmask1);
+ ATF_TP_ADD_TC(tp, eventmask2);
+ ATF_TP_ADD_TC(tp, eventmask3);
+ ATF_TP_ADD_TC(tp, eventmask4);
+ ATF_TP_ADD_TC(tp, eventmask5);
+ ATF_TP_ADD_TC(tp, eventmask6);
+
+ ATF_TP_ADD_TC_HAVE_PID(tp, fork1);
+ ATF_TP_ADD_TC(tp, fork2);
+
+ ATF_TP_ADD_TC_HAVE_PID(tp, vfork1);
+ ATF_TP_ADD_TC(tp, vfork2);
+
+ ATF_TP_ADD_TC(tp, vforkdone1);
+ ATF_TP_ADD_TC(tp, vforkdone2);
+
+ ATF_TP_ADD_TC(tp, io_read_d1);
+ ATF_TP_ADD_TC(tp, io_read_d2);
+ ATF_TP_ADD_TC(tp, io_read_d3);
+ ATF_TP_ADD_TC(tp, io_read_d4);
+
+ ATF_TP_ADD_TC(tp, io_write_d1);
+ ATF_TP_ADD_TC(tp, io_write_d2);
+ ATF_TP_ADD_TC(tp, io_write_d3);
+ ATF_TP_ADD_TC(tp, io_write_d4);
+
+ ATF_TP_ADD_TC(tp, read_d1);
+ ATF_TP_ADD_TC(tp, read_d2);
+ ATF_TP_ADD_TC(tp, read_d3);
+ ATF_TP_ADD_TC(tp, read_d4);
+
+ ATF_TP_ADD_TC(tp, write_d1);
+ ATF_TP_ADD_TC(tp, write_d2);
+ ATF_TP_ADD_TC(tp, write_d3);
+ ATF_TP_ADD_TC(tp, write_d4);
+
+ ATF_TP_ADD_TC(tp, io_read_d_write_d_handshake1);
+ ATF_TP_ADD_TC(tp, io_read_d_write_d_handshake2);
+
+ ATF_TP_ADD_TC(tp, read_d_write_d_handshake1);
+ ATF_TP_ADD_TC(tp, read_d_write_d_handshake2);
+
+ ATF_TP_ADD_TC(tp, io_read_i1);
+ ATF_TP_ADD_TC(tp, io_read_i2);
+ ATF_TP_ADD_TC(tp, io_read_i3);
+ ATF_TP_ADD_TC(tp, io_read_i4);
+
+ ATF_TP_ADD_TC(tp, read_i1);
+ ATF_TP_ADD_TC(tp, read_i2);
+ ATF_TP_ADD_TC(tp, read_i3);
+ ATF_TP_ADD_TC(tp, read_i4);
+
+ ATF_TP_ADD_TC(tp, io_read_auxv1);
+
+ ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs1);
+ ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs2);
+ ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs3);
+ ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs4);
+ ATF_TP_ADD_TC_HAVE_GPREGS(tp, regs5);
+
+ ATF_TP_ADD_TC_HAVE_FPREGS(tp, fpregs1);
+ ATF_TP_ADD_TC_HAVE_FPREGS(tp, fpregs2);
+
+ ATF_TP_ADD_TC_PT_STEP(tp, step1);
+ ATF_TP_ADD_TC_PT_STEP(tp, step2);
+ ATF_TP_ADD_TC_PT_STEP(tp, step3);
+ ATF_TP_ADD_TC_PT_STEP(tp, step4);
+
+ ATF_TP_ADD_TC(tp, kill1);
+ ATF_TP_ADD_TC(tp, kill2);
+
+ ATF_TP_ADD_TC(tp, lwpinfo1);
+ ATF_TP_ADD_TC_HAVE_PID(tp, lwpinfo2);
+
+ ATF_TP_ADD_TC(tp, siginfo1);
+ ATF_TP_ADD_TC(tp, siginfo2);
+ ATF_TP_ADD_TC(tp, siginfo3);
+ ATF_TP_ADD_TC(tp, siginfo4);
+ ATF_TP_ADD_TC_HAVE_PID(tp, siginfo5);
+ ATF_TP_ADD_TC_PT_STEP(tp, siginfo6);
+
+ ATF_TP_ADD_TC(tp, lwp_create1);
+
+ ATF_TP_ADD_TC(tp, lwp_exit1);
+
+ ATF_TP_ADD_TC(tp, signal1);
+ ATF_TP_ADD_TC(tp, signal2);
+ ATF_TP_ADD_TC(tp, signal3);
+ ATF_TP_ADD_TC_PT_STEP(tp, signal4);
+ ATF_TP_ADD_TC(tp, signal5);
+ ATF_TP_ADD_TC_HAVE_PID(tp, signal6);
+ ATF_TP_ADD_TC_HAVE_PID(tp, signal7);
+ ATF_TP_ADD_TC(tp, signal8);
+ ATF_TP_ADD_TC(tp, signal9);
+ ATF_TP_ADD_TC(tp, signal10);
+
+ ATF_TP_ADD_TC(tp, suspend1);
+ ATF_TP_ADD_TC(tp, suspend2);
+
+ ATF_TP_ADD_TC(tp, resume1);
+
+ ATF_TP_ADD_TC(tp, getsigmask1);
+ ATF_TP_ADD_TC(tp, getsigmask2);
+
+ ATF_TP_ADD_TC(tp, setsigmask1);
+ ATF_TP_ADD_TC(tp, setsigmask2);
+ ATF_TP_ADD_TC(tp, setsigmask3);
+ ATF_TP_ADD_TC(tp, setsigmask4);
+ ATF_TP_ADD_TC(tp, setsigmask5);
+ ATF_TP_ADD_TC(tp, setsigmask6);
+
+ ATF_TP_ADD_TC(tp, syscall1);
+
+ ATF_TP_ADD_TC(tp, syscallemu1);
+
+ ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64();
+ ATF_TP_ADD_TCS_PTRACE_WAIT_I386();
+ ATF_TP_ADD_TCS_PTRACE_WAIT_X86();
+
+ return atf_no_error();
+}
diff --git a/tests/lib/libc/sys/t_ptrace_wait.h b/tests/lib/libc/sys/t_ptrace_wait.h
new file mode 100644
index 00000000000..ae30417c258
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_wait.h
@@ -0,0 +1,431 @@
+/* $NetBSD: t_ptrace_wait.h,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/* Detect plain wait(2) use-case */
+#if !defined(TWAIT_WAITPID) && \
+ !defined(TWAIT_WAITID) && \
+ !defined(TWAIT_WAIT3) && \
+ !defined(TWAIT_WAIT4) && \
+ !defined(TWAIT_WAIT6)
+#define TWAIT_WAIT
+#endif
+
+/*
+ * There are two classes of wait(2)-like functions:
+ * - wait4(2)-like accepting pid_t, optional options parameter, struct rusage*
+ * - wait6(2)-like accepting idtype_t, id_t, struct wrusage, mandatory options
+ *
+ * The TWAIT_FNAME value is to be used for convenience in debug messages.
+ *
+ * The TWAIT_GENERIC() macro is designed to reuse the same unmodified
+ * code with as many wait(2)-like functions as possible.
+ *
+ * In a common use-case wait4(2) and wait6(2)-like function can work the almost
+ * the same way, however there are few important differences:
+ * wait6(2) must specify P_PID for idtype to match wpid from wait4(2).
+ * To behave like wait4(2), wait6(2) the 'options' to wait must include
+ * WEXITED|WTRUNCATED.
+ *
+ * There are two helper macros (they purpose it to mach more than one
+ * wait(2)-like function):
+ * The TWAIT_HAVE_STATUS - specifies whether a function can retrieve
+ * status (as integer value).
+ * The TWAIT_HAVE_PID - specifies whether a function can request
+ * exact process identifier
+ * The TWAIT_HAVE_RUSAGE - specifies whether a function can request
+ * the struct rusage value
+ *
+ */
+
+#if defined(TWAIT_WAIT)
+# define TWAIT_FNAME "wait"
+# define TWAIT_WAIT4TYPE(a,b,c,d) wait((b))
+# define TWAIT_GENERIC(a,b,c) wait((b))
+# define TWAIT_HAVE_STATUS 1
+#elif defined(TWAIT_WAITPID)
+# define TWAIT_FNAME "waitpid"
+# define TWAIT_WAIT4TYPE(a,b,c,d) waitpid((a),(b),(c))
+# define TWAIT_GENERIC(a,b,c) waitpid((a),(b),(c))
+# define TWAIT_HAVE_PID 1
+# define TWAIT_HAVE_STATUS 1
+#elif defined(TWAIT_WAITID)
+# define TWAIT_FNAME "waitid"
+# define TWAIT_GENERIC(a,b,c) \
+ waitid(P_PID,(a),NULL,(c)|WEXITED|WTRAPPED)
+# define TWAIT_WAIT6TYPE(a,b,c,d,e,f) waitid((a),(b),(f),(d))
+# define TWAIT_HAVE_PID 1
+#elif defined(TWAIT_WAIT3)
+# define TWAIT_FNAME "wait3"
+# define TWAIT_WAIT4TYPE(a,b,c,d) wait3((b),(c),(d))
+# define TWAIT_GENERIC(a,b,c) wait3((b),(c),NULL)
+# define TWAIT_HAVE_STATUS 1
+# define TWAIT_HAVE_RUSAGE 1
+#elif defined(TWAIT_WAIT4)
+# define TWAIT_FNAME "wait4"
+# define TWAIT_WAIT4TYPE(a,b,c,d) wait4((a),(b),(c),(d))
+# define TWAIT_GENERIC(a,b,c) wait4((a),(b),(c),NULL)
+# define TWAIT_HAVE_PID 1
+# define TWAIT_HAVE_STATUS 1
+# define TWAIT_HAVE_RUSAGE 1
+#elif defined(TWAIT_WAIT6)
+# define TWAIT_FNAME "wait6"
+# define TWAIT_WAIT6TYPE(a,b,c,d,e,f) wait6((a),(b),(c),(d),(e),(f))
+# define TWAIT_GENERIC(a,b,c) \
+ wait6(P_PID,(a),(b),(c)|WEXITED|WTRAPPED,NULL,NULL)
+# define TWAIT_HAVE_PID 1
+# define TWAIT_HAVE_STATUS 1
+#endif
+
+/*
+ * There are 3 groups of tests:
+ * - TWAIT_GENERIC() (wait, wait2, waitpid, wait3, wait4, wait6)
+ * - TWAIT_WAIT4TYPE() (wait2, waitpid, wait3, wait4)
+ * - TWAIT_WAIT6TYPE() (waitid, wait6)
+ *
+ * Tests only in the above categories are allowed. However some tests are not
+ * possible in the context requested functionality to be verified, therefore
+ * there are helper macros:
+ * - TWAIT_HAVE_PID (wait2, waitpid, waitid, wait4, wait6)
+ * - TWAIT_HAVE_STATUS (wait, wait2, waitpid, wait3, wait4, wait6)
+ * - TWAIT_HAVE_RUSAGE (wait3, wait4)
+ * - TWAIT_HAVE_RETPID (wait, wait2, waitpid, wait3, wait4, wait6)
+ *
+ * If there is an intention to test e.g. wait6(2) specific features in the
+ * ptrace(2) context, find the most matching group and with #ifdefs reduce
+ * functionality of less featured than wait6(2) interface (TWAIT_WAIT6TYPE).
+ *
+ * For clarity never use negative preprocessor checks, like:
+ * #if !defined(TWAIT_WAIT4)
+ * always refer to checks for positive values.
+ */
+
+#define TEST_REQUIRE_EQ(x, y) \
+do { \
+ uintmax_t vx = (x); \
+ uintmax_t vy = (y); \
+ int ret = vx == vy; \
+ if (!ret) \
+ ATF_REQUIRE_EQ_MSG(vx, vy, "%s(%ju) == %s(%ju)", \
+ #x, vx, #y, vy); \
+} while (/*CONSTCOND*/0)
+
+/*
+ * A child process cannot call atf functions and expect them to magically
+ * work like in the parent.
+ * The printf(3) messaging from a child will not work out of the box as well
+ * without estabilishing a communication protocol with its parent. To not
+ * overcomplicate the tests - do not log from a child and use err(3)/errx(3)
+ * wrapped with FORKEE_ASSERT()/FORKEE_ASSERTX() as that is guaranteed to work.
+ */
+#define FORKEE_ASSERT_EQ(x, y) \
+do { \
+ uintmax_t vx = (x); \
+ uintmax_t vy = (y); \
+ int ret = vx == vy; \
+ if (!ret) \
+ errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: " \
+ "%s(%ju) == %s(%ju)", __FILE__, __LINE__, __func__, \
+ #x, vx, #y, vy); \
+} while (/*CONSTCOND*/0)
+
+#define FORKEE_ASSERTX(x) \
+do { \
+ int ret = (x); \
+ if (!ret) \
+ errx(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",\
+ __FILE__, __LINE__, __func__, #x); \
+} while (/*CONSTCOND*/0)
+
+#define FORKEE_ASSERT(x) \
+do { \
+ int ret = (x); \
+ if (!ret) \
+ err(EXIT_FAILURE, "%s:%d %s(): Assertion failed for: %s",\
+ __FILE__, __LINE__, __func__, #x); \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Simplify logic for functions using general purpose registers add HAVE_GPREGS
+ *
+ * For platforms that do not implement all needed calls for simplicity assume
+ * that they are unsupported at all.
+ */
+#if defined(PT_GETREGS) \
+ && defined(PT_SETREGS) \
+ && defined(PTRACE_REG_PC) \
+ && defined(PTRACE_REG_SET_PC) \
+ && defined(PTRACE_REG_SP) \
+ && defined(PTRACE_REG_INTRV)
+#define HAVE_GPREGS
+#endif
+
+/* Add guards for floating point registers */
+#if defined(PT_GETFPREGS) \
+ && defined(PT_SETFPREGS)
+#define HAVE_FPREGS
+#endif
+
+/* Add guards for cpu debug registers */
+#if defined(PT_GETDBREGS) \
+ && defined(PT_SETDBREGS)
+#define HAVE_DBREGS
+#endif
+
+/*
+ * If waitid(2) returns because one or more processes have a state change to
+ * report, 0 is returned. If an error is detected, a value of -1 is returned
+ * and errno is set to indicate the error. If WNOHANG is specified and there
+ * are no stopped, continued or exited children, 0 is returned.
+ */
+#if defined(TWAIT_WAITID)
+#define TWAIT_REQUIRE_SUCCESS(a,b) TEST_REQUIRE_EQ((a), 0)
+#define TWAIT_REQUIRE_FAILURE(a,b) ATF_REQUIRE_ERRNO((a),(b) == -1)
+#define FORKEE_REQUIRE_SUCCESS(a,b) FORKEE_ASSERT_EQ(a, 0)
+#define FORKEE_REQUIRE_FAILURE(a,b) \
+ FORKEE_ASSERTX(((a) == errno) && ((b) == -1))
+#else
+#define TWAIT_REQUIRE_SUCCESS(a,b) TEST_REQUIRE_EQ((a), (b))
+#define TWAIT_REQUIRE_FAILURE(a,b) ATF_REQUIRE_ERRNO((a),(b) == -1)
+#define FORKEE_REQUIRE_SUCCESS(a,b) FORKEE_ASSERT_EQ(a, b)
+#define FORKEE_REQUIRE_FAILURE(a,b) \
+ FORKEE_ASSERTX(((a) == errno) && ((b) == -1))
+#endif
+
+/*
+ * Helper tools to verify whether status reports exited value
+ */
+#if TWAIT_HAVE_STATUS
+static void __used
+validate_status_exited(int status, int expected)
+{
+ ATF_REQUIRE_MSG(WIFEXITED(status), "Reported !exited process");
+ ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process");
+ ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process");
+ ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process");
+
+ ATF_REQUIRE_EQ_MSG(WEXITSTATUS(status), expected,
+ "The process has exited with invalid value %d != %d",
+ WEXITSTATUS(status), expected);
+}
+
+static void __used
+forkee_status_exited(int status, int expected)
+{
+ FORKEE_ASSERTX(WIFEXITED(status));
+ FORKEE_ASSERTX(!WIFCONTINUED(status));
+ FORKEE_ASSERTX(!WIFSIGNALED(status));
+ FORKEE_ASSERTX(!WIFSTOPPED(status));
+
+ FORKEE_ASSERT_EQ(WEXITSTATUS(status), expected);
+}
+
+static void __used
+validate_status_continued(int status)
+{
+ ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process");
+ ATF_REQUIRE_MSG(WIFCONTINUED(status), "Reported !continued process");
+ ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process");
+ ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process");
+}
+
+static void __used
+forkee_status_continued(int status)
+{
+ FORKEE_ASSERTX(!WIFEXITED(status));
+ FORKEE_ASSERTX(WIFCONTINUED(status));
+ FORKEE_ASSERTX(!WIFSIGNALED(status));
+ FORKEE_ASSERTX(!WIFSTOPPED(status));
+}
+
+static void __used
+validate_status_signaled(int status, int expected_termsig, int expected_core)
+{
+ ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process");
+ ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process");
+ ATF_REQUIRE_MSG(WIFSIGNALED(status), "Reported !signaled process");
+ ATF_REQUIRE_MSG(!WIFSTOPPED(status), "Reported stopped process");
+
+ ATF_REQUIRE_EQ_MSG(WTERMSIG(status), expected_termsig,
+ "Unexpected signal received");
+
+ ATF_REQUIRE_EQ_MSG(WCOREDUMP(status), expected_core,
+ "Unexpectedly core file %s generated", expected_core ? "not" : "");
+}
+
+static void __used
+forkee_status_signaled(int status, int expected_termsig, int expected_core)
+{
+ FORKEE_ASSERTX(!WIFEXITED(status));
+ FORKEE_ASSERTX(!WIFCONTINUED(status));
+ FORKEE_ASSERTX(WIFSIGNALED(status));
+ FORKEE_ASSERTX(!WIFSTOPPED(status));
+
+ FORKEE_ASSERT_EQ(WTERMSIG(status), expected_termsig);
+ FORKEE_ASSERT_EQ(WCOREDUMP(status), expected_core);
+}
+
+static void __used
+validate_status_stopped(int status, int expected)
+{
+ ATF_REQUIRE_MSG(!WIFEXITED(status), "Reported exited process");
+ ATF_REQUIRE_MSG(!WIFCONTINUED(status), "Reported continued process");
+ ATF_REQUIRE_MSG(!WIFSIGNALED(status), "Reported signaled process");
+ ATF_REQUIRE_MSG(WIFSTOPPED(status), "Reported !stopped process");
+
+ char st[128], ex[128];
+ strlcpy(st, strsignal(WSTOPSIG(status)), sizeof(st));
+ strlcpy(ex, strsignal(expected), sizeof(ex));
+
+ ATF_REQUIRE_EQ_MSG(WSTOPSIG(status), expected,
+ "Unexpected stop signal received [%s] != [%s]", st, ex);
+}
+
+static void __used
+forkee_status_stopped(int status, int expected)
+{
+ FORKEE_ASSERTX(!WIFEXITED(status));
+ FORKEE_ASSERTX(!WIFCONTINUED(status));
+ FORKEE_ASSERTX(!WIFSIGNALED(status));
+ FORKEE_ASSERTX(WIFSTOPPED(status));
+
+ FORKEE_ASSERT_EQ(WSTOPSIG(status), expected);
+}
+#else
+#define validate_status_exited(a,b)
+#define forkee_status_exited(a,b)
+#define validate_status_continued(a,b)
+#define forkee_status_continued(a,b)
+#define validate_status_signaled(a,b,c)
+#define forkee_status_signaled(a,b,c)
+#define validate_status_stopped(a,b)
+#define forkee_status_stopped(a,b)
+#endif
+
+/* This function is currently designed to be run in the main/parent process */
+static void __used
+await_zombie(pid_t process)
+{
+ struct kinfo_proc2 p;
+ size_t len = sizeof(p);
+
+ const int name[] = {
+ [0] = CTL_KERN,
+ [1] = KERN_PROC2,
+ [2] = KERN_PROC_PID,
+ [3] = process,
+ [4] = sizeof(p),
+ [5] = 1
+ };
+
+ const size_t namelen = __arraycount(name);
+
+ /* Await the process becoming a zombie */
+ while(1) {
+ ATF_REQUIRE(sysctl(name, namelen, &p, &len, NULL, 0) == 0);
+
+ if (p.p_stat == LSZOMB)
+ break;
+
+ ATF_REQUIRE(usleep(1000) == 0);
+ }
+}
+
+/* Happy number sequence -- this function is used to just consume cpu cycles */
+#define HAPPY_NUMBER 1
+
+/* If n is not happy then its sequence ends in the cycle:
+ * 4, 16, 37, 58, 89, 145, 42, 20, 4, ... */
+#define SAD_NUMBER 4
+
+/* Calculate the sum of the squares of the digits of n */
+static unsigned __used
+dsum(unsigned n)
+{
+ unsigned sum, x;
+ for (sum = 0; n; n /= 10) {
+ x = n % 10;
+ sum += x * x;
+ }
+ return sum;
+}
+
+/*
+ * XXX: Disabled optimization is required to make tests for hardware assisted
+ * traps in .text functional
+ *
+ * Tested with GCC 5.4 on NetBSD 7.99.47 amd64
+ */
+static int __used
+#ifdef __clang__
+__attribute__((__optnone__))
+#else
+__attribute__((__optimize__("O0")))
+#endif
+check_happy(unsigned n)
+{
+ for (;;) {
+ unsigned total = dsum(n);
+
+ if (total == HAPPY_NUMBER)
+ return 1;
+ if (total == SAD_NUMBER)
+ return 0;
+
+ n = total;
+ }
+}
+
+#if defined(TWAIT_HAVE_PID)
+#define ATF_TP_ADD_TC_HAVE_PID(a,b) ATF_TP_ADD_TC(a,b)
+#else
+#define ATF_TP_ADD_TC_HAVE_PID(a,b)
+#endif
+
+#if defined(HAVE_GPREGS)
+#define ATF_TP_ADD_TC_HAVE_GPREGS(a,b) ATF_TP_ADD_TC(a,b)
+#else
+#define ATF_TP_ADD_TC_HAVE_GPREGS(a,b)
+#endif
+
+#if defined(HAVE_FPREGS)
+#define ATF_TP_ADD_TC_HAVE_FPREGS(a,b) ATF_TP_ADD_TC(a,b)
+#else
+#define ATF_TP_ADD_TC_HAVE_FPREGS(a,b)
+#endif
+
+#if defined(HAVE_DBREGS)
+#define ATF_TP_ADD_TC_HAVE_DBREGS(a,b) ATF_TP_ADD_TC(a,b)
+#else
+#define ATF_TP_ADD_TC_HAVE_DBREGS(a,b)
+#endif
+
+#if defined(PT_STEP)
+#define ATF_TP_ADD_TC_PT_STEP(a,b) ATF_TP_ADD_TC(a,b)
+#else
+#define ATF_TP_ADD_TC_PT_STEP(a,b)
+#endif
diff --git a/tests/lib/libc/sys/t_ptrace_wait3.c b/tests/lib/libc/sys/t_ptrace_wait3.c
new file mode 100644
index 00000000000..c073cf47f27
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_wait3.c
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_wait3.c,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define TWAIT_WAIT3
+#include "t_ptrace_wait.c"
diff --git a/tests/lib/libc/sys/t_ptrace_wait4.c b/tests/lib/libc/sys/t_ptrace_wait4.c
new file mode 100644
index 00000000000..5fe0659bd65
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_wait4.c
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_wait4.c,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define TWAIT_WAIT4
+#include "t_ptrace_wait.c"
diff --git a/tests/lib/libc/sys/t_ptrace_wait6.c b/tests/lib/libc/sys/t_ptrace_wait6.c
new file mode 100644
index 00000000000..8d1bbe08cae
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_wait6.c
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_wait6.c,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define TWAIT_WAIT6
+#include "t_ptrace_wait.c"
diff --git a/tests/lib/libc/sys/t_ptrace_waitid.c b/tests/lib/libc/sys/t_ptrace_waitid.c
new file mode 100644
index 00000000000..73ac0585622
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_waitid.c
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_waitid.c,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define TWAIT_WAITID
+#include "t_ptrace_wait.c"
diff --git a/tests/lib/libc/sys/t_ptrace_waitpid.c b/tests/lib/libc/sys/t_ptrace_waitpid.c
new file mode 100644
index 00000000000..00c9462a6f9
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_waitpid.c
@@ -0,0 +1,30 @@
+/* $NetBSD: t_ptrace_waitpid.c,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define TWAIT_WAITPID
+#include "t_ptrace_wait.c"
diff --git a/tests/lib/libc/sys/t_ptrace_x86_wait.h b/tests/lib/libc/sys/t_ptrace_x86_wait.h
new file mode 100644
index 00000000000..a2a4aedf609
--- /dev/null
+++ b/tests/lib/libc/sys/t_ptrace_x86_wait.h
@@ -0,0 +1,1983 @@
+/* $NetBSD: t_ptrace_x86_wait.h,v 1.1 2017/04/02 21:44:00 kamil Exp $ */
+
+/*-
+ * Copyright (c) 2016 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+union u {
+ unsigned long raw;
+ struct {
+ unsigned long local_dr0_breakpoint : 1; /* 0 */
+ unsigned long global_dr0_breakpoint : 1; /* 1 */
+ unsigned long local_dr1_breakpoint : 1; /* 2 */
+ unsigned long global_dr1_breakpoint : 1; /* 3 */
+ unsigned long local_dr2_breakpoint : 1; /* 4 */
+ unsigned long global_dr2_breakpoint : 1; /* 5 */
+ unsigned long local_dr3_breakpoint : 1; /* 6 */
+ unsigned long global_dr3_breakpoint : 1; /* 7 */
+ unsigned long local_exact_breakpt : 1; /* 8 */
+ unsigned long global_exact_breakpt : 1; /* 9 */
+ unsigned long reserved_10 : 1; /* 10 */
+ unsigned long rest_trans_memory : 1; /* 11 */
+ unsigned long reserved_12 : 1; /* 12 */
+ unsigned long general_detect_enable : 1; /* 13 */
+ unsigned long reserved_14 : 1; /* 14 */
+ unsigned long reserved_15 : 1; /* 15 */
+ unsigned long condition_dr0 : 2; /* 16-17 */
+ unsigned long len_dr0 : 2; /* 18-19 */
+ unsigned long condition_dr1 : 2; /* 20-21 */
+ unsigned long len_dr1 : 2; /* 22-23 */
+ unsigned long condition_dr2 : 2; /* 24-25 */
+ unsigned long len_dr2 : 2; /* 26-27 */
+ unsigned long condition_dr3 : 2; /* 28-29 */
+ unsigned long len_dr3 : 2; /* 30-31 */
+ } bits;
+};
+
+ATF_TC(dbregs_print);
+ATF_TC_HEAD(dbregs_print, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify plain PT_GETDBREGS with printing Debug Registers");
+}
+
+ATF_TC_BODY(dbregs_print, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct dbreg r;
+ size_t i;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETDBREGS for the child process\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r, 0) != -1);
+
+ printf("State of the debug registers:\n");
+ for (i = 0; i < __arraycount(r.dr); i++)
+ printf("r[%zu]=%" PRIxREGISTER "\n", i, r.dr[i]);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+
+enum dbreg_preserve_mode {
+ dbreg_preserve_mode_none,
+ dbreg_preserve_mode_yield,
+ dbreg_preserve_mode_continued
+};
+
+static void
+dbreg_preserve(int reg, enum dbreg_preserve_mode mode)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct dbreg r1;
+ struct dbreg r2;
+ size_t i;
+ int watchme;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ if (mode == dbreg_preserve_mode_continued) {
+ printf("Before raising %s from child\n",
+ strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+ }
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+ printf("State of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ r1.dr[reg] = (long)(intptr_t)&watchme;
+ printf("Set DR0 (r1.dr[%d]) to new value %" PRIxREGISTER "\n",
+ reg, r1.dr[reg]);
+
+ printf("New state of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ switch (mode) {
+ case dbreg_preserve_mode_none:
+ break;
+ case dbreg_preserve_mode_yield:
+ printf("Yields a processor voluntarily and gives other "
+ "threads a chance to run without waiting for an "
+ "involuntary preemptive switch\n");
+ sched_yield();
+ break;
+ case dbreg_preserve_mode_continued:
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+ break;
+ }
+
+ printf("Call GETDBREGS for the child process (r2)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+ printf("Assert that (r1) and (r2) are the same\n");
+ ATF_REQUIRE(memcmp(&r1, &r2, sizeof(r1)) == 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+
+ATF_TC(dbregs_preserve_dr0);
+ATF_TC_HEAD(dbregs_preserve_dr0, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR0 is preserved across ptrace(2) calls");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr0, tc)
+{
+ dbreg_preserve(0, dbreg_preserve_mode_none);
+}
+
+ATF_TC(dbregs_preserve_dr1);
+ATF_TC_HEAD(dbregs_preserve_dr1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR1 is preserved across ptrace(2) calls");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr1, tc)
+{
+ dbreg_preserve(1, dbreg_preserve_mode_none);
+}
+
+ATF_TC(dbregs_preserve_dr2);
+ATF_TC_HEAD(dbregs_preserve_dr2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR2 is preserved across ptrace(2) calls");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr2, tc)
+{
+ dbreg_preserve(2, dbreg_preserve_mode_none);
+}
+
+ATF_TC(dbregs_preserve_dr3);
+ATF_TC_HEAD(dbregs_preserve_dr3, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR3 is preserved across ptrace(2) calls");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr3, tc)
+{
+ dbreg_preserve(3, dbreg_preserve_mode_none);
+}
+
+ATF_TC(dbregs_preserve_dr0_yield);
+ATF_TC_HEAD(dbregs_preserve_dr0_yield, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR0 is preserved across ptrace(2) calls with "
+ "scheduler yield");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr0_yield, tc)
+{
+ dbreg_preserve(0, dbreg_preserve_mode_yield);
+}
+
+ATF_TC(dbregs_preserve_dr1_yield);
+ATF_TC_HEAD(dbregs_preserve_dr1_yield, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR1 is preserved across ptrace(2) calls with "
+ "scheduler yield");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr1_yield, tc)
+{
+ dbreg_preserve(0, dbreg_preserve_mode_yield);
+}
+
+ATF_TC(dbregs_preserve_dr2_yield);
+ATF_TC_HEAD(dbregs_preserve_dr2_yield, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR2 is preserved across ptrace(2) calls with "
+ "scheduler yield");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr2_yield, tc)
+{
+ dbreg_preserve(0, dbreg_preserve_mode_yield);
+}
+
+
+ATF_TC(dbregs_preserve_dr3_yield);
+ATF_TC_HEAD(dbregs_preserve_dr3_yield, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR3 is preserved across ptrace(2) calls with "
+ "scheduler yield");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr3_yield, tc)
+{
+ dbreg_preserve(3, dbreg_preserve_mode_yield);
+}
+
+ATF_TC(dbregs_preserve_dr0_continued);
+ATF_TC_HEAD(dbregs_preserve_dr0_continued, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR0 is preserved across ptrace(2) calls and "
+ "with continued child");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr0_continued, tc)
+{
+ dbreg_preserve(0, dbreg_preserve_mode_continued);
+}
+
+ATF_TC(dbregs_preserve_dr1_continued);
+ATF_TC_HEAD(dbregs_preserve_dr1_continued, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR1 is preserved across ptrace(2) calls and "
+ "with continued child");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr1_continued, tc)
+{
+ dbreg_preserve(1, dbreg_preserve_mode_continued);
+}
+
+ATF_TC(dbregs_preserve_dr2_continued);
+ATF_TC_HEAD(dbregs_preserve_dr2_continued, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR2 is preserved across ptrace(2) calls and "
+ "with continued child");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr2_continued, tc)
+{
+ dbreg_preserve(2, dbreg_preserve_mode_continued);
+}
+
+ATF_TC(dbregs_preserve_dr3_continued);
+ATF_TC_HEAD(dbregs_preserve_dr3_continued, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting DR3 is preserved across ptrace(2) calls and "
+ "with continued child");
+}
+
+ATF_TC_BODY(dbregs_preserve_dr3_continued, tc)
+{
+ dbreg_preserve(3, dbreg_preserve_mode_continued);
+}
+
+
+static void
+dbregs_trap_variable(int reg, int cond, int len, bool write)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct dbreg r1;
+ size_t i;
+ volatile int watchme = 0;
+ union u dr7;
+
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ dr7.raw = 0;
+ switch (reg) {
+ case 0:
+ dr7.bits.global_dr0_breakpoint = 1;
+ dr7.bits.condition_dr0 = cond;
+ dr7.bits.len_dr0 = len;
+ case 1:
+ dr7.bits.global_dr1_breakpoint = 1;
+ dr7.bits.condition_dr1 = cond;
+ dr7.bits.len_dr1 = len;
+ case 2:
+ dr7.bits.global_dr2_breakpoint = 1;
+ dr7.bits.condition_dr2 = cond;
+ dr7.bits.len_dr2 = len;
+ case 3:
+ dr7.bits.global_dr3_breakpoint = 1;
+ dr7.bits.condition_dr3 = cond;
+ dr7.bits.len_dr3 = len;
+ break;
+ }
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ if (write)
+ watchme = 1;
+ else
+ printf("watchme=%d\n", watchme);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+ printf("State of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ r1.dr[reg] = (long)(intptr_t)&watchme;
+ printf("Set DR%d (r1.dr[%d]) to new value %" PRIxREGISTER "\n",
+ reg, reg, r1.dr[reg]);
+
+ r1.dr[7] = dr7.raw;
+ printf("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n",
+ r1.dr[7]);
+
+ printf("New state of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(dbregs_dr0_trap_variable_writeonly_byte);
+ATF_TC_HEAD(dbregs_dr0_trap_variable_writeonly_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on data writes only and 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_variable_writeonly_byte, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(0, 1, 0, true);
+}
+
+ATF_TC(dbregs_dr1_trap_variable_writeonly_byte);
+ATF_TC_HEAD(dbregs_dr1_trap_variable_writeonly_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on data writes only and 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_variable_writeonly_byte, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(1, 1, 0, true);
+}
+
+ATF_TC(dbregs_dr2_trap_variable_writeonly_byte);
+ATF_TC_HEAD(dbregs_dr2_trap_variable_writeonly_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on data writes only and 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_variable_writeonly_byte, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(2, 1, 0, true);
+}
+
+ATF_TC(dbregs_dr3_trap_variable_writeonly_byte);
+ATF_TC_HEAD(dbregs_dr3_trap_variable_writeonly_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on data writes only and 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_variable_writeonly_byte, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(3, 1, 0, true);
+}
+
+ATF_TC(dbregs_dr0_trap_variable_writeonly_2bytes);
+ATF_TC_HEAD(dbregs_dr0_trap_variable_writeonly_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on data writes only and 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_variable_writeonly_2bytes, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(0, 1, 1, true);
+}
+
+ATF_TC(dbregs_dr1_trap_variable_writeonly_2bytes);
+ATF_TC_HEAD(dbregs_dr1_trap_variable_writeonly_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on data writes only and 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_variable_writeonly_2bytes, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(1, 1, 1, true);
+}
+
+ATF_TC(dbregs_dr2_trap_variable_writeonly_2bytes);
+ATF_TC_HEAD(dbregs_dr2_trap_variable_writeonly_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on data writes only and 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_variable_writeonly_2bytes, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(2, 1, 1, true);
+}
+
+ATF_TC(dbregs_dr3_trap_variable_writeonly_2bytes);
+ATF_TC_HEAD(dbregs_dr3_trap_variable_writeonly_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on data writes only and 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_variable_writeonly_2bytes, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(3, 1, 1, true);
+}
+
+ATF_TC(dbregs_dr0_trap_variable_writeonly_4bytes);
+ATF_TC_HEAD(dbregs_dr0_trap_variable_writeonly_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on data writes only and 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_variable_writeonly_4bytes, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(0, 1, 3, true);
+}
+
+ATF_TC(dbregs_dr1_trap_variable_writeonly_4bytes);
+ATF_TC_HEAD(dbregs_dr1_trap_variable_writeonly_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on data writes only and 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_variable_writeonly_4bytes, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(1, 1, 3, true);
+}
+
+ATF_TC(dbregs_dr2_trap_variable_writeonly_4bytes);
+ATF_TC_HEAD(dbregs_dr2_trap_variable_writeonly_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on data writes only and 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_variable_writeonly_4bytes, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(2, 1, 3, true);
+}
+
+ATF_TC(dbregs_dr3_trap_variable_writeonly_4bytes);
+ATF_TC_HEAD(dbregs_dr3_trap_variable_writeonly_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on data writes only and 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_variable_writeonly_4bytes, tc)
+{
+ /* 0b01 -- break on data write only */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(3, 1, 3, true);
+}
+
+ATF_TC(dbregs_dr0_trap_variable_readwrite_write_byte);
+ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_write_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on data read/write trap in read 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_write_byte, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(0, 3, 0, true);
+}
+
+ATF_TC(dbregs_dr1_trap_variable_readwrite_write_byte);
+ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_write_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on data read/write trap in read 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_write_byte, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(1, 3, 0, true);
+}
+
+ATF_TC(dbregs_dr2_trap_variable_readwrite_write_byte);
+ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_write_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on data read/write trap in read 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_write_byte, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(2, 3, 0, true);
+}
+
+ATF_TC(dbregs_dr3_trap_variable_readwrite_write_byte);
+ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_write_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on data read/write trap in read 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_write_byte, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(3, 3, 0, true);
+}
+
+ATF_TC(dbregs_dr0_trap_variable_readwrite_write_2bytes);
+ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_write_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on data read/write trap in read 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_write_2bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(0, 3, 1, true);
+}
+
+ATF_TC(dbregs_dr1_trap_variable_readwrite_write_2bytes);
+ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_write_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on data read/write trap in read 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_write_2bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(1, 3, 1, true);
+}
+
+ATF_TC(dbregs_dr2_trap_variable_readwrite_write_2bytes);
+ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_write_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on data read/write trap in read 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_write_2bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(2, 3, 1, true);
+}
+
+ATF_TC(dbregs_dr3_trap_variable_readwrite_write_2bytes);
+ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_write_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on data read/write trap in read 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_write_2bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(3, 3, 1, true);
+}
+
+ATF_TC(dbregs_dr0_trap_variable_readwrite_write_4bytes);
+ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_write_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on data read/write trap in read 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_write_4bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(0, 3, 3, true);
+}
+
+ATF_TC(dbregs_dr1_trap_variable_readwrite_write_4bytes);
+ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_write_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on data read/write trap in read 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_write_4bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(1, 3, 3, true);
+}
+
+ATF_TC(dbregs_dr2_trap_variable_readwrite_write_4bytes);
+ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_write_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on data read/write trap in read 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_write_4bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(2, 3, 3, true);
+}
+
+ATF_TC(dbregs_dr3_trap_variable_readwrite_write_4bytes);
+ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_write_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on data read/write trap in read 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_write_4bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(3, 3, 3, true);
+}
+
+ATF_TC(dbregs_dr0_trap_variable_readwrite_read_byte);
+ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_read_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on data read/write trap in write 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_read_byte, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(0, 3, 0, false);
+}
+
+ATF_TC(dbregs_dr1_trap_variable_readwrite_read_byte);
+ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_read_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on data read/write trap in write 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_read_byte, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(1, 3, 0, false);
+}
+
+ATF_TC(dbregs_dr2_trap_variable_readwrite_read_byte);
+ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_read_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on data read/write trap in write 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_read_byte, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(2, 3, 0, false);
+}
+
+ATF_TC(dbregs_dr3_trap_variable_readwrite_read_byte);
+ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_read_byte, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on data read/write trap in write 1 byte mode)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_read_byte, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b00 -- 1 byte */
+
+ dbregs_trap_variable(3, 3, 0, false);
+}
+
+ATF_TC(dbregs_dr0_trap_variable_readwrite_read_2bytes);
+ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_read_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on data read/write trap in write 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_read_2bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(0, 3, 1, false);
+}
+
+ATF_TC(dbregs_dr1_trap_variable_readwrite_read_2bytes);
+ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_read_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on data read/write trap in write 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_read_2bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(1, 3, 1, false);
+}
+
+ATF_TC(dbregs_dr2_trap_variable_readwrite_read_2bytes);
+ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_read_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on data read/write trap in write 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_read_2bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(2, 3, 1, false);
+}
+
+ATF_TC(dbregs_dr3_trap_variable_readwrite_read_2bytes);
+ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_read_2bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on data read/write trap in write 2 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_read_2bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b01 -- 2 bytes */
+
+ dbregs_trap_variable(3, 3, 1, false);
+}
+
+ATF_TC(dbregs_dr0_trap_variable_readwrite_read_4bytes);
+ATF_TC_HEAD(dbregs_dr0_trap_variable_readwrite_read_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on data read/write trap in write 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_variable_readwrite_read_4bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(0, 3, 3, false);
+}
+
+ATF_TC(dbregs_dr1_trap_variable_readwrite_read_4bytes);
+ATF_TC_HEAD(dbregs_dr1_trap_variable_readwrite_read_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on data read/write trap in write 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_variable_readwrite_read_4bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(1, 3, 3, false);
+}
+
+ATF_TC(dbregs_dr2_trap_variable_readwrite_read_4bytes);
+ATF_TC_HEAD(dbregs_dr2_trap_variable_readwrite_read_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on data read/write trap in write 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_variable_readwrite_read_4bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(2, 3, 3, false);
+}
+
+ATF_TC(dbregs_dr3_trap_variable_readwrite_read_4bytes);
+ATF_TC_HEAD(dbregs_dr3_trap_variable_readwrite_read_4bytes, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on data read/write trap in write 4 bytes mode)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_read_4bytes, tc)
+{
+ /* 0b11 -- break on data write&read */
+ /* 0b11 -- 4 bytes */
+
+ dbregs_trap_variable(3, 3, 3, false);
+}
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_dr0_trap_code);
+ATF_TC_HEAD(dbregs_dr0_trap_code, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR0 triggers SIGTRAP "
+ "(break on code execution trap)");
+}
+
+ATF_TC_BODY(dbregs_dr0_trap_code, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct dbreg r1;
+ size_t i;
+ volatile int watchme = 1;
+ union u dr7;
+
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ dr7.raw = 0;
+ dr7.bits.global_dr0_breakpoint = 1;
+ dr7.bits.condition_dr0 = 0; /* 0b00 -- break on code execution */
+ dr7.bits.len_dr0 = 0; /* 0b00 -- 1 byte */
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("check_happy(%d)=%d\n", watchme, check_happy(watchme));
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+ printf("State of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ r1.dr[0] = (long)(intptr_t)check_happy;
+ printf("Set DR0 (r1.dr[0]) to new value %" PRIxREGISTER "\n",
+ r1.dr[0]);
+
+ r1.dr[7] = dr7.raw;
+ printf("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n",
+ r1.dr[7]);
+
+ printf("New state of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG);
+
+ printf("Remove code trap from check_happy=%p\n", check_happy);
+ dr7.bits.global_dr0_breakpoint = 0;
+ r1.dr[7] = dr7.raw;
+ printf("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n",
+ r1.dr[7]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_dr1_trap_code);
+ATF_TC_HEAD(dbregs_dr1_trap_code, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR1 triggers SIGTRAP "
+ "(break on code execution trap)");
+}
+
+ATF_TC_BODY(dbregs_dr1_trap_code, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct dbreg r1;
+ size_t i;
+ volatile int watchme = 1;
+ union u dr7;
+
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ dr7.raw = 0;
+ dr7.bits.global_dr1_breakpoint = 1;
+ dr7.bits.condition_dr1 = 0; /* 0b00 -- break on code execution */
+ dr7.bits.len_dr1 = 0; /* 0b00 -- 1 byte */
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("check_happy(%d)=%d\n", watchme, check_happy(watchme));
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+ printf("State of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ r1.dr[1] = (long)(intptr_t)check_happy;
+ printf("Set DR1 (r1.dr[1]) to new value %" PRIxREGISTER "\n",
+ r1.dr[1]);
+
+ r1.dr[7] = dr7.raw;
+ printf("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n",
+ r1.dr[7]);
+
+ printf("New state of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG);
+
+ printf("Remove code trap from check_happy=%p\n", check_happy);
+ dr7.bits.global_dr1_breakpoint = 0;
+ r1.dr[7] = dr7.raw;
+ printf("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n",
+ r1.dr[7]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_dr2_trap_code);
+ATF_TC_HEAD(dbregs_dr2_trap_code, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR2 triggers SIGTRAP "
+ "(break on code execution trap)");
+}
+
+ATF_TC_BODY(dbregs_dr2_trap_code, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct dbreg r1;
+ size_t i;
+ volatile int watchme = 1;
+ union u dr7;
+
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ dr7.raw = 0;
+ dr7.bits.global_dr2_breakpoint = 1;
+ dr7.bits.condition_dr2 = 0; /* 0b00 -- break on code execution */
+ dr7.bits.len_dr2 = 0; /* 0b00 -- 1 byte */
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("check_happy(%d)=%d\n", watchme, check_happy(watchme));
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+ printf("State of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ r1.dr[2] = (long)(intptr_t)check_happy;
+ printf("Set DR2 (r1.dr[2]) to new value %" PRIxREGISTER "\n",
+ r1.dr[2]);
+
+ r1.dr[7] = dr7.raw;
+ printf("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n",
+ r1.dr[7]);
+
+ printf("New state of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG);
+
+ printf("Remove code trap from check_happy=%p\n", check_happy);
+ dr7.bits.global_dr2_breakpoint = 0;
+ r1.dr[7] = dr7.raw;
+ printf("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n",
+ r1.dr[7]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+#if defined(HAVE_DBREGS)
+ATF_TC(dbregs_dr3_trap_code);
+ATF_TC_HEAD(dbregs_dr3_trap_code, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that setting trap with DR3 triggers SIGTRAP "
+ "(break on code execution trap)");
+}
+
+ATF_TC_BODY(dbregs_dr3_trap_code, tc)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ struct dbreg r1;
+ size_t i;
+ volatile int watchme = 1;
+ union u dr7;
+
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ dr7.raw = 0;
+ dr7.bits.global_dr3_breakpoint = 1;
+ dr7.bits.condition_dr3 = 0; /* 0b00 -- break on code execution */
+ dr7.bits.len_dr3 = 0; /* 0b00 -- 1 byte */
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("check_happy(%d)=%d\n", watchme, check_happy(watchme));
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+ printf("State of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ r1.dr[3] = (long)(intptr_t)check_happy;
+ printf("Set DR3 (r1.dr[3]) to new value %" PRIxREGISTER "\n",
+ r1.dr[3]);
+
+ r1.dr[7] = dr7.raw;
+ printf("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n",
+ r1.dr[7]);
+
+ printf("New state of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ printf("Before checking siginfo_t\n");
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, SIGTRAP);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_DBREG);
+
+ printf("Remove code trap from check_happy=%p\n", check_happy);
+ dr7.bits.global_dr3_breakpoint = 0;
+ r1.dr[7] = dr7.raw;
+ printf("Set DR7 (r1.dr[7]) to new value %" PRIxREGISTER "\n",
+ r1.dr[7]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Call CONTINUE for the child process\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+#endif
+
+volatile lwpid_t x86_the_lwp_id = 0;
+
+static void __used
+x86_lwp_main_func(void *arg)
+{
+ x86_the_lwp_id = _lwp_self();
+ _lwp_exit();
+}
+
+static void
+dbregs_dont_inherit_lwp(int reg)
+{
+ const int exitval = 5;
+ const int sigval = SIGSTOP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ ptrace_state_t state;
+ const int slen = sizeof(state);
+ ptrace_event_t event;
+ const int elen = sizeof(event);
+ ucontext_t uc;
+ lwpid_t lid;
+ static const size_t ssize = 16*1024;
+ void *stack;
+ size_t i;
+ struct dbreg r1;
+ struct dbreg r2;
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before allocating memory for stack in child\n");
+ FORKEE_ASSERT((stack = malloc(ssize)) != NULL);
+
+ printf("Before making context for new lwp in child\n");
+ _lwp_makecontext(&uc, x86_lwp_main_func, NULL, NULL, stack,
+ ssize);
+
+ printf("Before creating new in child\n");
+ FORKEE_ASSERT(_lwp_create(&uc, 0, &lid) == 0);
+
+ printf("Before waiting for lwp %d to exit\n", lid);
+ FORKEE_ASSERT(_lwp_wait(lid, NULL) == 0);
+
+ printf("Before verifying that reported %d and running lid %d "
+ "are the same\n", lid, x86_the_lwp_id);
+ FORKEE_ASSERT_EQ(lid, x86_the_lwp_id);
+
+ printf("Before exiting of the child process\n");
+ _exit(exitval);
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Set empty EVENT_MASK for the child %d\n", child);
+ event.pe_set_event = PTRACE_LWP_CREATE;
+ ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, child, &event, elen) != -1);
+
+ printf("Call GETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+ printf("State of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ r1.dr[reg] = (long)(intptr_t)check_happy;
+ printf("Set DR%d (r1.dr[%d]) to new value %" PRIxREGISTER "\n",
+ reg, reg, r1.dr[0]);
+
+ printf("New state of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected stopped "
+ "SIGTRAP\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, SIGTRAP);
+
+ ATF_REQUIRE(ptrace(PT_GET_PROCESS_STATE, child, &state, slen) != -1);
+
+ ATF_REQUIRE_EQ(state.pe_report_event, PTRACE_LWP_CREATE);
+
+ lid = state.pe_lwp;
+ printf("Reported PTRACE_LWP_CREATE event with lid %d\n", lid);
+
+ printf("Call GETDBREGS for the child process new lwp (r2)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, lid) != -1);
+
+ printf("State of the debug registers (r2):\n");
+ for (i = 0; i < __arraycount(r2.dr); i++)
+ printf("r2[%zu]=%" PRIxREGISTER "\n", i, r2.dr[i]);
+
+ printf("Assert that (r1) and (r2) are not the same\n");
+ ATF_REQUIRE(memcmp(&r1, &r2, sizeof(r1)) != 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child - expected exited\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_exited(status, exitval);
+
+ printf("Before calling %s() for the child - expected no process\n",
+ TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(dbregs_dr0_dont_inherit_lwp);
+ATF_TC_HEAD(dbregs_dr0_dont_inherit_lwp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that 1 LWP creation is intercepted by ptrace(2) with "
+ "EVENT_MASK set to PTRACE_LWP_CREATE and Debug Register 0 from "
+ "the forker thread is not inherited");
+}
+
+ATF_TC_BODY(dbregs_dr0_dont_inherit_lwp, tc)
+{
+ dbregs_dont_inherit_lwp(0);
+}
+
+ATF_TC(dbregs_dr1_dont_inherit_lwp);
+ATF_TC_HEAD(dbregs_dr1_dont_inherit_lwp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that 1 LWP creation is intercepted by ptrace(2) with "
+ "EVENT_MASK set to PTRACE_LWP_CREATE and Debug Register 1 from "
+ "the forker thread is not inherited");
+}
+
+ATF_TC_BODY(dbregs_dr1_dont_inherit_lwp, tc)
+{
+ dbregs_dont_inherit_lwp(1);
+}
+
+ATF_TC(dbregs_dr2_dont_inherit_lwp);
+ATF_TC_HEAD(dbregs_dr2_dont_inherit_lwp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that 1 LWP creation is intercepted by ptrace(2) with "
+ "EVENT_MASK set to PTRACE_LWP_CREATE and Debug Register 2 from "
+ "the forker thread is not inherited");
+}
+
+ATF_TC_BODY(dbregs_dr2_dont_inherit_lwp, tc)
+{
+ dbregs_dont_inherit_lwp(2);
+}
+
+ATF_TC(dbregs_dr3_dont_inherit_lwp);
+ATF_TC_HEAD(dbregs_dr3_dont_inherit_lwp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that 1 LWP creation is intercepted by ptrace(2) with "
+ "EVENT_MASK set to PTRACE_LWP_CREATE and Debug Register 3 from "
+ "the forker thread is not inherited");
+}
+
+ATF_TC_BODY(dbregs_dr3_dont_inherit_lwp, tc)
+{
+ dbregs_dont_inherit_lwp(3);
+}
+
+static void
+dbregs_dont_inherit_execve(int reg)
+{
+ const int sigval = SIGTRAP;
+ pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+ int status;
+#endif
+ size_t i;
+ struct dbreg r1;
+ struct dbreg r2;
+
+ struct ptrace_siginfo info;
+ memset(&info, 0, sizeof(info));
+
+ printf("Before forking process PID=%d\n", getpid());
+ ATF_REQUIRE((child = fork()) != -1);
+ if (child == 0) {
+ printf("Before calling PT_TRACE_ME from child %d\n", getpid());
+ FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
+
+ printf("Before raising %s from child\n", strsignal(sigval));
+ FORKEE_ASSERT(raise(sigval) == 0);
+
+ printf("Before calling execve(2) from child\n");
+ execlp("/bin/echo", "/bin/echo", NULL);
+
+ FORKEE_ASSERT(0 && "Not reached");
+ }
+ printf("Parent process PID=%d, child's PID=%d\n", getpid(), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Call GETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r1, 0) != -1);
+
+ printf("State of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ r1.dr[reg] = (long)(intptr_t)check_happy;
+ printf("Set DR%d (r1.dr[%d]) to new value %" PRIxREGISTER "\n",
+ reg, reg, r1.dr[reg]);
+
+ printf("New state of the debug registers (r1):\n");
+ for (i = 0; i < __arraycount(r1.dr); i++)
+ printf("r1[%zu]=%" PRIxREGISTER "\n", i, r1.dr[i]);
+
+ printf("Call SETDBREGS for the child process (r1)\n");
+ ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ validate_status_stopped(status, sigval);
+
+ printf("Before calling ptrace(2) with PT_GET_SIGINFO for child\n");
+ ATF_REQUIRE(ptrace(PT_GET_SIGINFO, child, &info, sizeof(info)) != -1);
+
+ printf("Signal traced to lwpid=%d\n", info.psi_lwpid);
+ printf("Signal properties: si_signo=%#x si_code=%#x si_errno=%#x\n",
+ info.psi_siginfo.si_signo, info.psi_siginfo.si_code,
+ info.psi_siginfo.si_errno);
+
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_signo, sigval);
+ ATF_REQUIRE_EQ(info.psi_siginfo.si_code, TRAP_EXEC);
+
+ printf("Call GETDBREGS for the child process after execve(2)\n");
+ ATF_REQUIRE(ptrace(PT_GETDBREGS, child, &r2, 0) != -1);
+
+ printf("State of the debug registers (r2):\n");
+ for (i = 0; i < __arraycount(r2.dr); i++)
+ printf("r2[%zu]=%" PRIxREGISTER "\n", i, r2.dr[i]);
+
+ printf("Assert that (r1) and (r2) are not the same\n");
+ ATF_REQUIRE(memcmp(&r1, &r2, sizeof(r1)) != 0);
+
+ printf("Before resuming the child process where it left off and "
+ "without signal to be sent\n");
+ ATF_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
+
+ printf("Before calling %s() for the child\n", TWAIT_FNAME);
+ TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
+}
+
+ATF_TC(dbregs_dr0_dont_inherit_execve);
+ATF_TC_HEAD(dbregs_dr0_dont_inherit_execve, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that execve(2) is intercepted by tracer and Debug "
+ "Register 0 is reset");
+}
+
+ATF_TC_BODY(dbregs_dr0_dont_inherit_execve, tc)
+{
+ dbregs_dont_inherit_execve(0);
+}
+
+ATF_TC(dbregs_dr1_dont_inherit_execve);
+ATF_TC_HEAD(dbregs_dr1_dont_inherit_execve, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that execve(2) is intercepted by tracer and Debug "
+ "Register 1 is reset");
+}
+
+ATF_TC_BODY(dbregs_dr1_dont_inherit_execve, tc)
+{
+ dbregs_dont_inherit_execve(1);
+}
+
+ATF_TC(dbregs_dr2_dont_inherit_execve);
+ATF_TC_HEAD(dbregs_dr2_dont_inherit_execve, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that execve(2) is intercepted by tracer and Debug "
+ "Register 2 is reset");
+}
+
+ATF_TC_BODY(dbregs_dr2_dont_inherit_execve, tc)
+{
+ dbregs_dont_inherit_execve(2);
+}
+
+ATF_TC(dbregs_dr3_dont_inherit_execve);
+ATF_TC_HEAD(dbregs_dr3_dont_inherit_execve, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that execve(2) is intercepted by tracer and Debug "
+ "Register 3 is reset");
+}
+
+ATF_TC_BODY(dbregs_dr3_dont_inherit_execve, tc)
+{
+ dbregs_dont_inherit_execve(3);
+}
+#define ATF_TP_ADD_TCS_PTRACE_WAIT_X86() \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_print); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr1); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr2); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr3); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0_yield); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr1_yield); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr2_yield); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr3_yield); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr0_continued); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr1_continued); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr2_continued); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_preserve_dr3_continued); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_writeonly_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_writeonly_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_writeonly_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_writeonly_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_writeonly_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_writeonly_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_writeonly_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_writeonly_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_writeonly_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_writeonly_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_writeonly_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_writeonly_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_write_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_write_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_write_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_write_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_write_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_write_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_write_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_write_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_write_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_write_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_write_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_write_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_read_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_read_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_read_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_read_byte); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_read_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_read_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_read_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_read_2bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_variable_readwrite_read_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_variable_readwrite_read_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_variable_readwrite_read_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_variable_readwrite_read_4bytes); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_trap_code); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_trap_code); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_trap_code); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_trap_code); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_dont_inherit_lwp); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_dont_inherit_lwp); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_dont_inherit_lwp); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_dont_inherit_lwp); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr0_dont_inherit_execve); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr1_dont_inherit_execve); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr2_dont_inherit_execve); \
+ ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs_dr3_dont_inherit_execve);
+#else
+#define ATF_TP_ADD_TCS_PTRACE_WAIT_X86()
+#endif