From 24e714f4354b03c156f20fd1e5b25f3b3b96ea8e Mon Sep 17 00:00:00 2001 From: kamil Date: Sun, 2 Apr 2017 21:44:00 +0000 Subject: [PATCH] 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 Sponsored by --- distrib/sets/lists/debug/mi | 9 +- distrib/sets/lists/tests/mi | 9 +- tests/lib/libc/sys/Makefile | 17 +- tests/lib/libc/sys/msg.h | 136 + tests/lib/libc/sys/t_ptrace.c | 208 + tests/lib/libc/sys/t_ptrace_amd64_wait.h | 114 + tests/lib/libc/sys/t_ptrace_i386_wait.h | 107 + tests/lib/libc/sys/t_ptrace_wait.c | 7590 ++++++++++++++++++++++ tests/lib/libc/sys/t_ptrace_wait.h | 431 ++ tests/lib/libc/sys/t_ptrace_wait3.c | 30 + tests/lib/libc/sys/t_ptrace_wait4.c | 30 + tests/lib/libc/sys/t_ptrace_wait6.c | 30 + tests/lib/libc/sys/t_ptrace_waitid.c | 30 + tests/lib/libc/sys/t_ptrace_waitpid.c | 30 + tests/lib/libc/sys/t_ptrace_x86_wait.h | 1983 ++++++ 15 files changed, 10750 insertions(+), 4 deletions(-) create mode 100644 tests/lib/libc/sys/msg.h create mode 100644 tests/lib/libc/sys/t_ptrace.c create mode 100644 tests/lib/libc/sys/t_ptrace_amd64_wait.h create mode 100644 tests/lib/libc/sys/t_ptrace_i386_wait.h create mode 100644 tests/lib/libc/sys/t_ptrace_wait.c create mode 100644 tests/lib/libc/sys/t_ptrace_wait.h create mode 100644 tests/lib/libc/sys/t_ptrace_wait3.c create mode 100644 tests/lib/libc/sys/t_ptrace_wait4.c create mode 100644 tests/lib/libc/sys/t_ptrace_wait6.c create mode 100644 tests/lib/libc/sys/t_ptrace_waitid.c create mode 100644 tests/lib/libc/sys/t_ptrace_waitpid.c create mode 100644 tests/lib/libc/sys/t_ptrace_x86_wait.h diff --git a/distrib/sets/lists/debug/mi b/distrib/sets/lists/debug/mi index cf87be5c0953..ea36ab1bf747 100644 --- a/distrib/sets/lists/debug/mi +++ b/distrib/sets/lists/debug/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.202 2017/04/02 01:49:51 kamil Exp $ +# $NetBSD: mi,v 1.203 2017/04/02 21:44:00 kamil Exp $ ./etc/mtree/set.debug comp-sys-root ./usr/lib comp-sys-usr compatdir ./usr/lib/i18n/libBIG5_g.a comp-c-debuglib debuglib,compatfile @@ -2079,6 +2079,13 @@ ./usr/libdata/debug/usr/tests/lib/libc/sys/t_poll.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/sys/t_posix_fadvise.debug tests-lib-debug debug,atf,rump ./usr/libdata/debug/usr/tests/lib/libc/sys/t_posix_fallocate.debug tests-lib-debug debug,atf,compattestfile +./usr/libdata/debug/usr/tests/lib/libc/sys/t_ptrace.debug tests-lib-debug debug,atf,compattestfile +./usr/libdata/debug/usr/tests/lib/libc/sys/t_ptrace_wait.debug tests-lib-debug debug,atf,compattestfile +./usr/libdata/debug/usr/tests/lib/libc/sys/t_ptrace_wait3.debug tests-lib-debug debug,atf,compattestfile +./usr/libdata/debug/usr/tests/lib/libc/sys/t_ptrace_wait4.debug tests-lib-debug debug,atf,compattestfile +./usr/libdata/debug/usr/tests/lib/libc/sys/t_ptrace_wait6.debug tests-lib-debug debug,atf,compattestfile +./usr/libdata/debug/usr/tests/lib/libc/sys/t_ptrace_waitid.debug tests-lib-debug debug,atf,compattestfile +./usr/libdata/debug/usr/tests/lib/libc/sys/t_ptrace_waitpid.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/sys/t_recvmmsg.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/sys/t_revoke.debug tests-lib-debug debug,atf,compattestfile ./usr/libdata/debug/usr/tests/lib/libc/sys/t_select.debug tests-lib-debug debug,atf,compattestfile diff --git a/distrib/sets/lists/tests/mi b/distrib/sets/lists/tests/mi index dbfe3e28c265..d1dc8154306a 100644 --- a/distrib/sets/lists/tests/mi +++ b/distrib/sets/lists/tests/mi @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.731 2017/04/02 01:49:52 kamil Exp $ +# $NetBSD: mi,v 1.732 2017/04/02 21:44:00 kamil Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -2762,6 +2762,13 @@ ./usr/tests/lib/libc/sys/t_poll tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/sys/t_posix_fadvise tests-lib-tests atf,rump ./usr/tests/lib/libc/sys/t_posix_fallocate tests-lib-tests compattestfile,atf +./usr/tests/lib/libc/sys/t_ptrace tests-lib-tests compattestfile,atf +./usr/tests/lib/libc/sys/t_ptrace_wait tests-lib-tests compattestfile,atf +./usr/tests/lib/libc/sys/t_ptrace_wait3 tests-lib-tests compattestfile,atf +./usr/tests/lib/libc/sys/t_ptrace_wait4 tests-lib-tests compattestfile,atf +./usr/tests/lib/libc/sys/t_ptrace_wait6 tests-lib-tests compattestfile,atf +./usr/tests/lib/libc/sys/t_ptrace_waitid tests-lib-tests compattestfile,atf +./usr/tests/lib/libc/sys/t_ptrace_waitpid tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/sys/t_recvmmsg tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/sys/t_revoke tests-lib-tests compattestfile,atf ./usr/tests/lib/libc/sys/t_select tests-lib-tests compattestfile,atf diff --git a/tests/lib/libc/sys/Makefile b/tests/lib/libc/sys/Makefile index 6f2f59a86fdc..e3f21b898da1 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 000000000000..184949af0cf8 --- /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 000000000000..76b1ac9c29bd --- /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 +__RCSID("$NetBSD: t_ptrace.c,v 1.1 2017/04/02 21:44:00 kamil Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 000000000000..62ce21ae66b5 --- /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 000000000000..eb7823d987bf --- /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 000000000000..c60a125bf1b9 --- /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 +__RCSID("$NetBSD: t_ptrace_wait.c,v 1.1 2017/04/02 21:44:00 kamil Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 000000000000..ae30417c2584 --- /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 000000000000..c073cf47f27f --- /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 000000000000..5fe0659bd654 --- /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 000000000000..8d1bbe08cae2 --- /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 000000000000..73ac0585622e --- /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 000000000000..00c9462a6f98 --- /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 000000000000..a2a4aedf6093 --- /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