diff options
Diffstat (limited to 'sys/amd64/amd64/trap.c')
-rw-r--r-- | sys/amd64/amd64/trap.c | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c index a553fc5..af4925a 100644 --- a/sys/amd64/amd64/trap.c +++ b/sys/amd64/amd64/trap.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); */ #include "opt_clock.h" +#include "opt_compat.h" #include "opt_cpu.h" #include "opt_hwpmc_hooks.h" #include "opt_isa.h" @@ -99,6 +100,11 @@ PMC_SOFT_DEFINE( , , page_fault, write); #include <sys/dtrace_bsd.h> #endif +extern inthand_t IDTVEC(bpt), IDTVEC(bpt_pti), IDTVEC(dbg), + IDTVEC(fast_syscall), IDTVEC(fast_syscall_pti), IDTVEC(fast_syscall32), + IDTVEC(int0x80_syscall_pti), IDTVEC(int0x80_syscall); + + void __noinline trap(struct trapframe *frame); void trap_check(struct trapframe *frame); void dblfault_handler(struct trapframe *frame); @@ -535,6 +541,52 @@ trap(struct trapframe *frame) load_dr6(rdr6() & ~0xf); return; } + + /* + * Malicious user code can configure a debug + * register watchpoint to trap on data access + * to the top of stack and then execute 'pop + * %ss; int 3'. Due to exception deferral for + * 'pop %ss', the CPU will not interrupt 'int + * 3' to raise the DB# exception for the debug + * register but will postpone the DB# until + * execution of the first instruction of the + * BP# handler (in kernel mode). Normally the + * previous check would ignore DB# exceptions + * for watchpoints on user addresses raised in + * kernel mode. However, some CPU errata + * include cases where DB# exceptions do not + * properly set bits in %dr6, e.g. Haswell + * HSD23 and Skylake-X SKZ24. + * + * A deferred DB# can also be raised on the + * first instructions of system call entry + * points or single-step traps via similar use + * of 'pop %ss' or 'mov xxx, %ss'. + */ + if (pti) { + if (frame->tf_rip == + (uintptr_t)IDTVEC(fast_syscall_pti) || +#ifdef COMPAT_FREEBSD32 + frame->tf_rip == + (uintptr_t)IDTVEC(int0x80_syscall_pti) || +#endif + frame->tf_rip == (uintptr_t)IDTVEC(bpt_pti)) + return; + } else { + if (frame->tf_rip == + (uintptr_t)IDTVEC(fast_syscall) || +#ifdef COMPAT_FREEBSD32 + frame->tf_rip == + (uintptr_t)IDTVEC(int0x80_syscall) || +#endif + frame->tf_rip == (uintptr_t)IDTVEC(bpt)) + return; + } + if (frame->tf_rip == (uintptr_t)IDTVEC(dbg) || + /* Needed for AMD. */ + frame->tf_rip == (uintptr_t)IDTVEC(fast_syscall32)) + return; /* * FALLTHROUGH (TRCTRAP kernel mode, kernel address) */ |