Created
December 24, 2019 21:26
-
-
Save Keno/e0231a5f6f8e255eff4842931d555325 to your computer and use it in GitHub Desktop.
watchpoint.c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "util.h" | |
#define ARRAY_SIZE(a) \ | |
((sizeof(a) / sizeof(*(a))) / \ | |
!(sizeof(a) % sizeof(*(a)))) | |
struct DebugControl { | |
uintptr_t dr0_local : 1; | |
uintptr_t dr0_global : 1; | |
uintptr_t dr1_local : 1; | |
uintptr_t dr1_global : 1; | |
uintptr_t dr2_local : 1; | |
uintptr_t dr2_global : 1; | |
uintptr_t dr3_local : 1; | |
uintptr_t dr3_global : 1; | |
uintptr_t ignored : 8; | |
uintptr_t dr0_type : 2; | |
uintptr_t dr0_len : 2; | |
uintptr_t dr1_type : 2; | |
uintptr_t dr1_len : 2; | |
uintptr_t dr2_type : 2; | |
uintptr_t dr2_len : 2; | |
uintptr_t dr3_type : 2; | |
uintptr_t dr3_len : 2; | |
}; | |
struct PackedDebugControl { | |
union { | |
struct DebugControl ctrl; | |
uintptr_t val; | |
}; | |
}; | |
struct test_instance { | |
size_t start_offset; | |
size_t break_offset; | |
size_t n; | |
}; | |
static void breakpoint(void) | |
{ | |
__asm__("int $3"); | |
} | |
static void do_memset(void *start, size_t n) | |
{ | |
uintptr_t a = 0; | |
__asm__("rep stosb\n\t" ::"a"(a), "c"(n), "D"(start)); | |
} | |
static void cont_wait_stop(pid_t child) { | |
int status; | |
test_assert(0 == ptrace(PTRACE_CONT, child, NULL, NULL)); | |
test_assert(child == waitpid(child, &status, 0)); | |
test_assert(status == ((SIGTRAP << 8) | 0x7f)); | |
} | |
static void advance_rip(pid_t child) { | |
struct user_regs_struct regs; | |
struct iovec iov; | |
pid_t wret; | |
iov.iov_base = ®s; | |
iov.iov_len = sizeof(regs); | |
test_assert(0 == ptrace(PTRACE_GETREGSET, child, (void*)NT_PRSTATUS, &iov)); | |
regs.rip += 1; | |
test_assert(0 == ptrace(PTRACE_SETREGSET, child, (void*)NT_PRSTATUS, &iov)); | |
} | |
int main(void) { | |
pid_t child; | |
int status; | |
int pipe_fds[2]; | |
struct test_instance *program; | |
size_t nprograms; | |
// Setup program | |
nprograms = 0x2020; | |
program = malloc(nprograms * sizeof(struct test_instance)); | |
for (int i = 0; i < nprograms; ++i) { | |
program[i].start_offset = 0; | |
program[i].break_offset = i; | |
program[i].n = 0x2020; | |
} | |
test_assert(0 == pipe(pipe_fds)); | |
size_t max_size = 0; | |
for (int i = 0; i < nprograms; ++i) { | |
if (program[i].n > max_size) | |
max_size = program[i].n; | |
} | |
void *ptr = mmap(NULL, max_size, PROT_READ | PROT_WRITE, | |
MAP_SHARED | MAP_ANONYMOUS, -1, 0); | |
if (0 == (child = fork())) { | |
char ch; | |
read(pipe_fds[0], &ch, 1); | |
for (int i = 0; i < nprograms; ++i) { | |
breakpoint(); | |
do_memset(ptr+program[i].start_offset, program[i].n); | |
} | |
return 77; | |
} | |
test_assert(0 == ptrace(PTRACE_ATTACH, child, NULL, NULL)); | |
test_assert(child == waitpid(child, &status, 0)); | |
test_assert(status == ((SIGSTOP << 8) | 0x7f)); | |
test_assert(1 == write(pipe_fds[1], "x", 1)); | |
atomic_printf("N\tOvershoot\n"); | |
for (int i = 0; i < nprograms; ++i) { | |
// Wait till we're stopped at the breakpoint | |
cont_wait_stop(child); | |
advance_rip(child); | |
// Setup watchpoint | |
test_assert(0 == ptrace(PTRACE_POKEUSER, child, | |
(void*)offsetof(struct user, u_debugreg[0]), | |
(void*)ptr + program[i].break_offset)); | |
struct PackedDebugControl ctrl; | |
assert(sizeof(struct DebugControl) == sizeof(uintptr_t)); | |
memset(&ctrl, 0, sizeof(struct PackedDebugControl)); | |
ctrl.ctrl.dr0_local = 1; | |
ctrl.ctrl.dr0_type = 0b01; | |
//ctrl.ctrl.dr0_len = 0; | |
test_assert(0 == ptrace(PTRACE_POKEUSER, child, | |
(void*)offsetof(struct user, u_debugreg[7]), | |
(void*)ctrl.val)); | |
// Continue to watchpoint | |
test_assert(0 == ptrace(PTRACE_CONT, child, NULL, NULL)); | |
test_assert(child == waitpid(child, &status, 0)); | |
// Read rdi | |
struct user_regs_struct regs; | |
struct iovec iov; | |
pid_t wret; | |
iov.iov_base = ®s; | |
iov.iov_len = sizeof(regs); | |
test_assert(0 == ptrace(PTRACE_GETREGSET, child, (void*)NT_PRSTATUS, &iov)); | |
atomic_printf("%ld\t%lld\n", program[i].break_offset, regs.rdi-program[i].break_offset-(uintptr_t)ptr); | |
// Clear watchpoint | |
test_assert(0 == ptrace(PTRACE_POKEUSER, child, | |
(void*)offsetof(struct user, u_debugreg[7]), | |
(void*)0)); | |
} | |
test_assert(0 == ptrace(PTRACE_DETACH, child, NULL, NULL)); | |
test_assert(child == waitpid(child, &status, 0)); | |
test_assert(WIFEXITED(status)); | |
test_assert(WEXITSTATUS(status) == 77); | |
atomic_puts("EXIT-SUCCESS"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment