DF-5721 / probe_dbregs.c
/* DF-5721 probe 3: (a) PT_SETDBREGS with DR0 = kernel address must be * rejected (set_dbregs, machdep.c:3190-3211); (b) a legal user-space hardware * watchpoint whose buffer the kernel writes during a syscall (read(2) copyout) * must be swallowed by user_dbreg_trap() (trap.c:735-742) without entering * DDB or wedging. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <sys/mman.h> #include <machine/reg.h> #define WATCH_ADDR 0x10000000UL int main(void) { pid_t pid; int st, pfd[2]; struct dbreg d; long r; pipe(pfd); pid = fork(); if (pid == 0) { char *buf = mmap((void *)WATCH_ADDR, 4096, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0); if (buf != (char *)WATCH_ADDR) _exit(10); ptrace(PT_TRACE_ME, 0, (caddr_t)1, 0); kill(getpid(), SIGTRAP); /* stop #1 */ /* kernel writes 4 bytes to WATCH_ADDR via read() copyout; * DR0 watchpoint is armed on it */ read(pfd[0], buf, 4); int3_marker: __asm__ __volatile__("int3"); _exit(0); } write(pfd[1], "AAAA", 4); close(pfd[1]); wait(&st); printf("dbregs: initial stop sig=%d\n", WSTOPSIG(st)); /* (a) kernel address must fail */ memset(&d, 0, sizeof(d)); d.dr[0] = 0xffffffff80100000UL; /* kernel text */ d.dr[7] = 0xD0001; /* L0, RW=write, LEN=4 */ errno = 0; r = ptrace(PT_SETDBREGS, pid, (caddr_t)&d, 0); printf("dbregs: SETDBREGS(kernel addr 0xffffffff80100000) = %ld errno=%d (%s)\n", r, errno, r < 0 ? strerror(errno) : "ACCEPTED -- BAD"); if (r == 0) { printf("dbregs: !!! KERNEL-ADDRESS WATCHPOINT ARMED -- filter test skipped\n"); memset(&d, 0, sizeof(d)); } /* (b) legal user address; kernel write via syscall must be swallowed */ memset(&d, 0, sizeof(d)); d.dr[0] = WATCH_ADDR; d.dr[7] = 0xD0001; errno = 0; r = ptrace(PT_SETDBREGS, pid, (caddr_t)&d, 0); printf("dbregs: SETDBREGS(user addr %p) = %ld errno=%d (%s)\n", (void *)WATCH_ADDR, r, errno, r < 0 ? strerror(errno) : "ok"); if (r < 0) { printf("dbregs: cannot arm user watchpoint; filter test inconclusive\n"); kill(pid, SIGKILL); return 0; } /* let the child run read() -> kernel copyout hits DR0 */ ptrace(PT_CONTINUE, pid, (caddr_t)1, 0); wait(&st); printf("dbregs: after syscall-write-to-watched-buffer: stopped=%d sig=%d%s\n", WIFSTOPPED(st), WIFSTOPPED(st) ? WSTOPSIG(st) : 0, WIFSTOPPED(st) && WSTOPSIG(st) == SIGTRAP ? " (watchpoint delivered to user: also fine)" : WIFSIGNALED(st) ? " (CHILD DIED -- BAD)" : ""); ptrace(PT_CONTINUE, pid, (caddr_t)1, 0); wait(&st); printf("dbregs: final status=%d\n", st); /* disarm before exit */ memset(&d, 0, sizeof(d)); ptrace(PT_SETDBREGS, pid, (caddr_t)&d, 0); return 0; } |