/* Test case for setting a memory-write watchpoint. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely. */ /* It may sometimes fail (between 1% and 100% of cases for me) even on a kernel generally working, expecting the same bug affecting `ppc-dabr-race' even while this testcase does not use threads. Testcase returns rc 2 on a missed (therefore unsupported) watchpoint. */ #define _GNU_SOURCE 1 #ifdef __ia64__ #define ia64_fpreg ia64_fpreg_DISABLE #define pt_all_user_regs pt_all_user_regs_DISABLE #endif /* __ia64__ */ #include #ifdef __ia64__ #undef ia64_fpreg #undef pt_all_user_regs #endif /* __ia64__ */ #include #include #include #if defined __i386__ || defined __x86_64__ #include #endif #include #include #include #include #include #include #include #include #ifdef __powerpc__ #define SET_WATCHPOINT set_watchpoint #ifndef PTRACE_GET_DEBUGREG # define PTRACE_GET_DEBUGREG 25 #endif #ifndef PTRACE_SET_DEBUGREG # define PTRACE_SET_DEBUGREG 26 #endif static void set_watchpoint (pid_t pid, volatile void *addr) { long dabr; long l; errno = 0; l = ptrace (PTRACE_GET_DEBUGREG, pid, 0l, &dabr); if (l == -1l) { assert (errno == EIO); /* Missing kernel/hw support should get this. */ exit (77); } else { assert_perror (errno); assert (l == 0); assert (dabr == 0); } dabr = (unsigned long) addr; dabr |= 7; l = ptrace (PTRACE_SET_DEBUGREG, pid, 0l, dabr); assert_perror (errno); assert (l == 0); } #elif defined __x86_64__ || defined __i386__ #define SET_WATCHPOINT set_watchpoint static void set_watchpoint (pid_t pid, volatile void *addr) { unsigned long dr7; long l; errno = 0; l = ptrace (PTRACE_POKEUSER, pid, offsetof (struct user, u_debugreg[0]), (unsigned long) addr); assert_perror (errno); assert (l == 0); dr7 = (DR_RW_WRITE << DR_CONTROL_SHIFT); #ifdef DR_LEN_8 /* * For a 32-bit build, DR_LEN_8 might be defined by the header. * On a 64-bit kernel, we might even be able to use it. * But we can't tell, and we don't really need it, so just use DR_LEN_4. */ if (sizeof (long) > 4) dr7 |= (DR_LEN_8 << DR_CONTROL_SHIFT); else #endif dr7 |= (DR_LEN_4 << DR_CONTROL_SHIFT); dr7 |= (1UL << DR_LOCAL_ENABLE_SHIFT); dr7 |= (1UL << DR_GLOBAL_ENABLE_SHIFT); l = ptrace (PTRACE_POKEUSER, pid, offsetof (struct user, u_debugreg[7]), dr7); assert_perror (errno); assert (l == 0); } #endif #ifndef SET_WATCHPOINT int main (void) { return 77; } #else static pid_t child; static void cleanup (void) { if (child > 0) kill (child, SIGKILL); child = 0; } static void handler_fail (int signo) { cleanup (); signal (signo, SIG_DFL); raise (signo); } static void expect_trap_and_signal (int expect) { long l; pid_t got_pid; int status; errno = 0; l = ptrace (PTRACE_CONT, child, 0l, 0l); assert_perror (errno); assert (l == 0); errno = 0; got_pid = waitpid (child, &status, 0); assert_perror (errno); assert (got_pid == child); assert (WIFSTOPPED (status)); if (WSTOPSIG (status) == expect) exit (1); assert (WSTOPSIG (status) == SIGTRAP); errno = 0; l = ptrace (PTRACE_CONT, child, 0l, 0l); assert_perror (errno); assert (l == 0); errno = 0; got_pid = waitpid (child, &status, 0); assert_perror (errno); assert (got_pid == child); assert (WIFSTOPPED (status)); assert (WSTOPSIG (status) == expect); } int main (void) { pid_t got_pid; int i, status; long l; int pagesize = getpagesize (); unsigned char *page, *page_got; atexit (cleanup); signal (SIGABRT, handler_fail); signal (SIGINT, handler_fail); assert (pagesize > 1); errno = 0; page = mmap (NULL, 2 * pagesize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); assert_perror (errno); assert (page != MAP_FAILED); child = fork (); switch (child) { case -1: assert (0); case 0: l = ptrace (PTRACE_TRACEME, 0, NULL, NULL); assert (l == 0); i = raise (SIGUSR1); assert (i == 0); errno = 0; i = mprotect (page, pagesize, PROT_NONE); assert_perror (errno); assert (i == 0); i = raise (SIGUSR1); assert (i == 0); errno = 0; i = mprotect (page, pagesize, PROT_READ | PROT_WRITE); assert_perror (errno); assert (i == 0); i = raise (SIGUSR1); assert (i == 0); errno = 0; page_got = mremap (page, pagesize, 2 * pagesize, MREMAP_MAYMOVE); assert_perror (errno); assert (page_got != MAP_FAILED); assert (page_got != page); i = raise (SIGUSR1); assert (i == 0); errno = 0; page_got = mmap (page, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); assert_perror (errno); assert (page_got == page); i = raise (SIGUSR1); assert (i == 0); errno = 0; i = munmap (page, pagesize); assert_perror (errno); assert (i == 0); i = raise (SIGUSR2); assert (i == 0); /* NOTREACHED */ assert (0); default: break; } errno = 0; got_pid = waitpid (child, &status, 0); assert_perror (errno); assert (got_pid == child); assert (WIFSTOPPED (status)); assert (WSTOPSIG (status) == SIGUSR1); SET_WATCHPOINT (child, page); /* mprotect (page, pagesize, PROT_NONE); */ expect_trap_and_signal (SIGUSR1); /* mprotect (page, pagesize, PROT_READ | PROT_WRITE); */ expect_trap_and_signal (SIGUSR1); /* mremap (page, pagesize, 2 * pagesize, MREMAP_MAYMOVE); */ expect_trap_and_signal (SIGUSR1); /* mmap (page, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); */ expect_trap_and_signal (SIGUSR1); /* munmap (page, pagesize); */ expect_trap_and_signal (SIGUSR2); return 0; } #endif