Module Name: src Committed By: kamil Date: Sat Dec 3 01:41:15 UTC 2016
Modified Files: src/tests/kernel/arch/amd64: t_ptrace_wait.c Log Message: Define new tests for CPU Debug Registers in t_ptrace_wait{,3,4,6,id,pid} Rename dbregs1 to dbregs_print Rename dbregs[2345] to dbregs_preserve_dr[0123] Add new tests dbregs_preserve_dr[0123]_yield. dbregs_preserve_dr0_yield: Verify that setting DR0 is preserved across ptrace(2) calls with scheduler yield dbregs_preserve_dr1_yield: Verify that setting DR1 is preserved across ptrace(2) calls with scheduler yield dbregs_preserve_dr2_yield: Verify that setting DR2 is preserved across ptrace(2) calls with scheduler yield dbregs_preserve_dr3_yield: Verify that setting DR3 is preserved across ptrace(2) calls with scheduler yield Add new tests dbregs_preserve_dr[0123]_continued. dbregs_preserve_dr0_continued: Verify that setting DR0 is preserved across ptrace(2) calls and with continued child dbregs_preserve_dr1_continued: Verify that setting DR1 is preserved across ptrace(2) calls and with continued child dbregs_preserve_dr2_continued: Verify that setting DR2 is preserved across ptrace(2) calls and with continued child dbregs_preserve_dr3_continued: Verify that setting DR3 is preserved across ptrace(2) calls and with continued child Use more meaningful names for these tests as they are MD specific and testing precise functionality. Also there will be a growing number of tests in this category and prefixing everything with plain dbregs and trailing with a number cannot be verbose. Sponsored by <The NetBSD Foundation> To generate a diff of this commit: cvs rdiff -u -r1.2 -r1.3 src/tests/kernel/arch/amd64/t_ptrace_wait.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/tests/kernel/arch/amd64/t_ptrace_wait.c diff -u src/tests/kernel/arch/amd64/t_ptrace_wait.c:1.2 src/tests/kernel/arch/amd64/t_ptrace_wait.c:1.3 --- src/tests/kernel/arch/amd64/t_ptrace_wait.c:1.2 Fri Dec 2 06:49:00 2016 +++ src/tests/kernel/arch/amd64/t_ptrace_wait.c Sat Dec 3 01:41:15 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: t_ptrace_wait.c,v 1.2 2016/12/02 06:49:00 kamil Exp $ */ +/* $NetBSD: t_ptrace_wait.c,v 1.3 2016/12/03 01:41:15 kamil Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: t_ptrace_wait.c,v 1.2 2016/12/02 06:49:00 kamil Exp $"); +__RCSID("$NetBSD: t_ptrace_wait.c,v 1.3 2016/12/03 01:41:15 kamil Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -39,6 +39,7 @@ __RCSID("$NetBSD: t_ptrace_wait.c,v 1.2 #include <machine/reg.h> #include <err.h> #include <errno.h> +#include <sched.h> #include <signal.h> #include <stdint.h> #include <stdio.h> @@ -53,14 +54,14 @@ __RCSID("$NetBSD: t_ptrace_wait.c,v 1.2 #include "../../t_ptrace_wait.h" #if defined(HAVE_DBREGS) -ATF_TC(dbregs1); -ATF_TC_HEAD(dbregs1, tc) +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(dbregs1, tc) +ATF_TC_BODY(dbregs_print, tc) { const int exitval = 5; const int sigval = SIGSTOP; @@ -112,14 +113,14 @@ ATF_TC_BODY(dbregs1, tc) #endif #if defined(HAVE_DBREGS) -ATF_TC(dbregs2); -ATF_TC_HEAD(dbregs2, tc) +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(dbregs2, tc) +ATF_TC_BODY(dbregs_preserve_dr0, tc) { const int exitval = 5; const int sigval = SIGSTOP; @@ -196,14 +197,14 @@ ATF_TC_BODY(dbregs2, tc) #endif #if defined(HAVE_DBREGS) -ATF_TC(dbregs3); -ATF_TC_HEAD(dbregs3, tc) +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(dbregs3, tc) +ATF_TC_BODY(dbregs_preserve_dr1, tc) { const int exitval = 5; const int sigval = SIGSTOP; @@ -280,14 +281,14 @@ ATF_TC_BODY(dbregs3, tc) #endif #if defined(HAVE_DBREGS) -ATF_TC(dbregs4); -ATF_TC_HEAD(dbregs4, tc) +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(dbregs4, tc) +ATF_TC_BODY(dbregs_preserve_dr2, tc) { const int exitval = 5; const int sigval = SIGSTOP; @@ -364,14 +365,14 @@ ATF_TC_BODY(dbregs4, tc) #endif #if defined(HAVE_DBREGS) -ATF_TC(dbregs5); -ATF_TC_HEAD(dbregs5, tc) +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(dbregs5, tc) +ATF_TC_BODY(dbregs_preserve_dr3, tc) { const int exitval = 5; const int sigval = SIGSTOP; @@ -447,16 +448,771 @@ ATF_TC_BODY(dbregs5, tc) } #endif +#if defined(HAVE_DBREGS) +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) +{ + 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; + /* Number of available CPU Debug Registers on AMD64 */ + const size_t len = 16; + size_t i; + int watchme; + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len); + ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len); + + printf("Before forking process PID=%d\n", getpid()); + child = atf_utils_fork(); + 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 (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.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + r1.dbregs[0] = (long)(intptr_t)&watchme; + printf("Set DR0 (r1.dbregs[0]) to new value %#lx\n", r1.dbregs[0]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + printf("Call SETDBREGS for the child process (r1)\n"); + ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + printf("Yields a processor voluntarily and gives other threads a " + "chance to run without waiting for an involuntary preemptive " + "switch\n"); + sched_yield(); + + 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, 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)); +} +#endif + +#if defined(HAVE_DBREGS) +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) +{ + 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; + /* Number of available CPU Debug Registers on AMD64 */ + const size_t len = 16; + size_t i; + int watchme; + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len); + ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len); + + printf("Before forking process PID=%d\n", getpid()); + child = atf_utils_fork(); + 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 (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.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + r1.dbregs[1] = (long)(intptr_t)&watchme; + printf("Set DR1 (r1.dbregs[1]) to new value %#lx\n", r1.dbregs[1]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + printf("Call SETDBREGS for the child process (r1)\n"); + ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + printf("Yields a processor voluntarily and gives other threads a " + "chance to run without waiting for an involuntary preemptive " + "switch\n"); + sched_yield(); + + 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, 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)); +} +#endif + +#if defined(HAVE_DBREGS) +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) +{ + 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; + /* Number of available CPU Debug Registers on AMD64 */ + const size_t len = 16; + size_t i; + int watchme; + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len); + ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len); + + printf("Before forking process PID=%d\n", getpid()); + child = atf_utils_fork(); + 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 (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.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + r1.dbregs[2] = (long)(intptr_t)&watchme; + printf("Set DR2 (r1.dbregs[2]) to new value %#lx\n", r1.dbregs[2]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + printf("Call SETDBREGS for the child process (r1)\n"); + ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + printf("Yields a processor voluntarily and gives other threads a " + "chance to run without waiting for an involuntary preemptive " + "switch\n"); + sched_yield(); + + 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, 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)); +} +#endif + +#if defined(HAVE_DBREGS) +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) +{ + 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; + /* Number of available CPU Debug Registers on AMD64 */ + const size_t len = 16; + size_t i; + int watchme; + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len); + ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len); + + printf("Before forking process PID=%d\n", getpid()); + child = atf_utils_fork(); + 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 (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.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + r1.dbregs[3] = (long)(intptr_t)&watchme; + printf("Set DR3 (r1.dbregs[3]) to new value %#lx\n", r1.dbregs[3]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + printf("Call SETDBREGS for the child process (r1)\n"); + ATF_REQUIRE(ptrace(PT_SETDBREGS, child, &r1, 0) != -1); + + printf("Yields a processor voluntarily and gives other threads a " + "chance to run without waiting for an involuntary preemptive " + "switch\n"); + sched_yield(); + + 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, 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)); +} +#endif + +#if defined(HAVE_DBREGS) +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) +{ + 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; + /* Number of available CPU Debug Registers on AMD64 */ + const size_t len = 16; + size_t i; + int watchme; + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len); + ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len); + + printf("Before forking process PID=%d\n", getpid()); + child = atf_utils_fork(); + 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("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.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + r1.dbregs[0] = (long)(intptr_t)&watchme; + printf("Set DR0 (r1.dbregs[0]) to new value %#lx\n", r1.dbregs[0]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[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, sigval); + + 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, 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)); +} +#endif + +#if defined(HAVE_DBREGS) +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) +{ + 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; + /* Number of available CPU Debug Registers on AMD64 */ + const size_t len = 16; + size_t i; + int watchme; + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len); + ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len); + + printf("Before forking process PID=%d\n", getpid()); + child = atf_utils_fork(); + 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("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.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + r1.dbregs[1] = (long)(intptr_t)&watchme; + printf("Set DR1 (r1.dbregs[1]) to new value %#lx\n", r1.dbregs[1]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[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, sigval); + + 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, 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)); +} +#endif + +#if defined(HAVE_DBREGS) +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) +{ + 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; + /* Number of available CPU Debug Registers on AMD64 */ + const size_t len = 16; + size_t i; + int watchme; + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len); + ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len); + + printf("Before forking process PID=%d\n", getpid()); + child = atf_utils_fork(); + 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("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.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + r1.dbregs[2] = (long)(intptr_t)&watchme; + printf("Set DR2 (r1.dbregs[2]) to new value %#lx\n", r1.dbregs[2]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[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, sigval); + + 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, 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)); +} +#endif + +#if defined(HAVE_DBREGS) +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) +{ + 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; + /* Number of available CPU Debug Registers on AMD64 */ + const size_t len = 16; + size_t i; + int watchme; + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dbregs), len); + ATF_REQUIRE_EQ(__arraycount(r2.dbregs), len); + + printf("Before forking process PID=%d\n", getpid()); + child = atf_utils_fork(); + 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("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.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[i]); + + r1.dbregs[3] = (long)(intptr_t)&watchme; + printf("Set DR3 (r1.dbregs[3]) to new value %#lx\n", r1.dbregs[3]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dbregs); i++) + printf("r1[%zu]=%#lx\n", i, r1.dbregs[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, sigval); + + 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, 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)); +} +#endif + ATF_TP_ADD_TCS(tp) { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); - ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs1); - ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs2); - ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs3); - ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs4); - ATF_TP_ADD_TC_HAVE_DBREGS(tp, dbregs5); + 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); return atf_no_error(); }