diff --git a/tests/kernel/arch/amd64/t_ptrace_wait.c b/tests/kernel/arch/amd64/t_ptrace_wait.c index 62191518419e..6b9f77196ffe 100644 --- a/tests/kernel/arch/amd64/t_ptrace_wait.c +++ b/tests/kernel/arch/amd64/t_ptrace_wait.c @@ -1,4 +1,4 @@ -/* $NetBSD: t_ptrace_wait.c,v 1.17 2017/02/20 01:21:47 kamil Exp $ */ +/* $NetBSD: t_ptrace_wait.c,v 1.18 2017/02/20 01:34:53 kamil Exp $ */ /*- * Copyright (c) 2016 The NetBSD Foundation, Inc. @@ -27,7 +27,7 @@ */ #include -__RCSID("$NetBSD: t_ptrace_wait.c,v 1.17 2017/02/20 01:21:47 kamil Exp $"); +__RCSID("$NetBSD: t_ptrace_wait.c,v 1.18 2017/02/20 01:34:53 kamil Exp $"); #include #include @@ -4426,6 +4426,1566 @@ ATF_TC_BODY(dbregs_dr3_trap_variable_readwrite_write_4bytes, tc) } #endif +#if defined(HAVE_DBREGS) +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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr0 = 0; /* 0b00 -- 1 byte */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[0] = (long)(intptr_t)&watchme; + printf("Set DR0 (r1.dr[0]) to new value %#lx\n", r1.dr[0]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr1 = 0; /* 0b00 -- 1 byte */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[1] = (long)(intptr_t)&watchme; + printf("Set DR1 (r1.dr[1]) to new value %#lx\n", r1.dr[1]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr2 = 0; /* 0b00 -- 1 byte */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[2] = (long)(intptr_t)&watchme; + printf("Set DR2 (r1.dr[2]) to new value %#lx\n", r1.dr[2]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr3 = 0; /* 0b00 -- 1 byte */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[3] = (long)(intptr_t)&watchme; + printf("Set DR3 (r1.dr[3]) to new value %#lx\n", r1.dr[3]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr0 = 1; /* 0b01 -- 2 bytes */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[0] = (long)(intptr_t)&watchme; + printf("Set DR0 (r1.dr[0]) to new value %#lx\n", r1.dr[0]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr1 = 1; /* 0b01 -- 2 bytes */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[1] = (long)(intptr_t)&watchme; + printf("Set DR1 (r1.dr[1]) to new value %#lx\n", r1.dr[1]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr2 = 1; /* 0b01 -- 2 bytes */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[2] = (long)(intptr_t)&watchme; + printf("Set DR2 (r1.dr[2]) to new value %#lx\n", r1.dr[2]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr3 = 1; /* 0b01 -- 2 bytes */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[3] = (long)(intptr_t)&watchme; + printf("Set DR3 (r1.dr[3]) to new value %#lx\n", r1.dr[3]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr0 = 3; /* 0b11 -- 4 bytes */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[0] = (long)(intptr_t)&watchme; + printf("Set DR0 (r1.dr[0]) to new value %#lx\n", r1.dr[0]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr1 = 3; /* 0b11 -- 4 bytes */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[1] = (long)(intptr_t)&watchme; + printf("Set DR1 (r1.dr[1]) to new value %#lx\n", r1.dr[1]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr2 = 3; /* 0b11 -- 4 bytes */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[2] = (long)(intptr_t)&watchme; + printf("Set DR2 (r1.dr[2]) to new value %#lx\n", r1.dr[2]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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_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) +{ + 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; + 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 = 3; /* 0b11 -- break on data write&read */ + dr7.bits.len_dr3 = 3; /* 0b11 -- 4 bytes */ + + printf("Assert that known number of Debug Registers (%zu) is valid\n", + len); + ATF_REQUIRE_EQ(__arraycount(r1.dr), len); + ATF_REQUIRE_EQ(__arraycount(r2.dr), len); + + 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("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]=%#lx\n", i, r1.dr[i]); + + r1.dr[3] = (long)(intptr_t)&watchme; + printf("Set DR3 (r1.dr[3]) to new value %#lx\n", r1.dr[3]); + + r1.dr[7] = dr7.raw; + printf("Set DR7 (r1.dr[7]) to new value %#lx\n", r1.dr[7]); + + printf("New state of the debug registers (r1):\n"); + for (i = 0; i < __arraycount(r1.dr); i++) + printf("r1[%zu]=%#lx\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("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); @@ -4480,5 +6040,20 @@ ATF_TP_ADD_TCS(tp) 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); + return atf_no_error(); }