kernel - Fix CVE-2018-8897, debug register issue
authorMatthew Dillon <dillon@apollo.backplane.com>
Tue, 1 May 2018 03:47:20 +0000 (20:47 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Tue, 8 May 2018 17:00:20 +0000 (10:00 -0700)
* #DB can be delayed in a way that causes it to occur on the first
  instruction of the int $3 or syscall handlers.  These handlers must
  be able to detect and handle the condition.  This is a historical
  artifact of cpu operation that has existed for a very long time on
  both AMD and Intel CPUs.

* Fix by giving #DB its own trampoline stack and a way to load a
  deterministic %gs and %cr3 independent of the normal CS check.
  This is CVE-2018-8897.

* Also fix the NMI trampoline while I'm here.

* Also fix an old issue with debug register trace traps which can
  occur when the kernel is accessing the user's address space.
  This fix was lost years ago, now recovered.

Credits: Nick Peterson of Everdox Tech, LLC (original reporter)
Credits: Thanks to Microsoft for coordinating the OS vendor response

sys/cpu/x86_64/include/asmacros.h
sys/cpu/x86_64/include/frame.h
sys/platform/pc64/include/globaldata.h
sys/platform/pc64/x86_64/exception.S
sys/platform/pc64/x86_64/genassym.c
sys/platform/pc64/x86_64/machdep.c
sys/platform/pc64/x86_64/mp_machdep.c
sys/platform/pc64/x86_64/pmap.c
sys/platform/pc64/x86_64/trap.c

index fb84979..d46fe35 100644 (file)
  *                 XXX If IBPB is not supported, try to clear the
  *                 call return hw cache w/ many x chained call sequence?
  *
- *     IBRS2 note - We are leaving IBRS on full-time.  However, Intel
- *     believes it is not safe unless the MSR is poked on each user->kernel
- *     transition, so poke the MSR for both IBRS1 and IBRS2.
+ * NOTE - IBRS2 - We are leaving IBRS on full-time.  However, Intel
+ *               believes it is not safe unless the MSR is poked on each
+ *               user->kernel transition, so poke the MSR for both IBRS1
+ *               and IBRS2.
  */
 #define KMMUENTER_CORE                                                 \
        testq   $PCB_ISOMMU,PCPU(trampoline)+TR_PCB_FLAGS ;             \
 /*
  * PUSH_FRAME is the first thing executed upon interrupt entry.  We are
  * responsible for swapgs execution and the KMMUENTER dispatch.
+ *
+ * NOTE - PUSH_FRAME code doesn't mess with %gs or the stack, or assume it can
+ *       use PCPU(trampoline), if the trap/exception is from supevisor mode.
+ *       It only messes with that stuff when the trap/exception is from user
+ *       mode.  Our DBG and NMI code depend on this behavior.
  */
 #define PUSH_FRAME_TFRIP                                               \
        testb   $SEL_RPL_MASK,TF_CS-TF_RIP(%rsp) ; /* from userland? */ \
index 49a753d..e485326 100644 (file)
@@ -1,7 +1,7 @@
 /*-
  * Copyright (c) 2003 Peter Wemm.
  * Copyright (c) 1990 The Regents of the University of California.
- * Copyright (c) 2008 The DragonFly Project.
+ * Copyright (c) 2008-2018 The DragonFly Project.
  * All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
@@ -145,7 +145,9 @@ struct trampframe {
        register_t      tr_pcb_cr3_iso; /* copy of isolated pml4e */
        register_t      tr_pcb_cr3;     /* copy of primary pml4e */
        register_t      tr_pcb_gflags;  /* global flags (IBRS support) */
-       register_t      tr_pcb_unused01;
+       register_t      tr_pcb_gs_kernel; /* (used by nmi, dbg) */
+       register_t      tr_pcb_gs_saved;  /* (used by nmi) */
+       register_t      tr_pcb_cr3_saved; /* (used by nmi) */
 };
 
 int    kdb_trap(int, int, struct trapframe *);
index fcfdcb5..c2873c2 100644 (file)
@@ -139,14 +139,20 @@ struct privatespace {
                                  sizeof(struct x86_64tss)];
        struct trampframe trampoline;
        uint64_t        reserved1b;     /* 16-byte-align trampoline */
-       struct x86_64tss common_tss;
+       struct x86_64tss common_tss;    /* (misaligned by 8 bytes) */
 
        /*
-        * page 3, 4 - Double fault stack
+        * page 3, 4 - NMI, Double fault stack
+        * page 5, 6 - Debug fault stack
         */
-       char            dblstack[PAGE_SIZE * 2];
-
-       /* page 5..4+UPAGES - idle stack (UPAGES pages) */
+       char            dblstack[PAGE_SIZE * 2 -
+                                sizeof(struct trampframe)];
+       struct trampframe dbltramp;
+       char            dbgstack[PAGE_SIZE * 2 -
+                                sizeof(struct trampframe)];
+       struct trampframe dbgtramp;
+
+       /* page 7+   - idle stack (UPAGES pages) */
        char            idlestack[UPAGES * PAGE_SIZE];
 } __packed;
 
index 1d27def..944d220 100644 (file)
@@ -102,8 +102,153 @@ MCOUNT_LABEL(btrap)
        movq $0,TF_XFLAGS(%rsp) ;               \
        jmp alltraps
 
+/*
+ * Due to a historical artifact, it is possible for a #DB exception
+ * to occur in certain bad places that would normlally be protected by
+ * the interrupt gate's interrupt disablement.
+ *
+ * Due to this possibly occuring in the system call entry code, we also
+ * run #DB on an ist2 stack to force the cpu to load a new %rsp, otherwise
+ * it might push the cpu exception frame onto the user stack.  To make things
+ * easier we just point ist2 at our trampoline area.
+ */
 IDTVEC(dbg)
+#ifdef DIRECT_DISALLOW_SS_CPUBUG
+       /*
+        * Directly disallow #DB faults which can occur at critical points
+        * in the code due to a historical artifact of how the cpu operates.
+        * %gs state might not match RPL.  Test the %rip and iretq immediately
+        * (valid %gs and %cr3 state not needed).  If we don't need kernel
+        * reporting we can enable this and its a bit safer from unintended
+        * consequences.
+        *
+        * If this is not enabled the kernel still catches the problem.  It
+        * will report the problem and continue properly.
+        */
+       cmpq    $Xbpt,0(%rsp)
+       je      200f
+       cmpq    $Xfast_syscall,0(%rsp)
+       je      200f
+#endif
+
+       /*
+        * Ok, regardless of the RPL mask in the trap frame, we took
+        * the trap on a separate stack via ist2.  This means we
+        * must copy it appropriately.
+        *
+        * If coming from userland we can skip directly to the normal
+        * TRAP code because it will handle the fact that we are on an
+        * alternative stack (dbgstack set by ist2), even though it isn't
+        * the trampoline stack).  The frame will be moved to the correct
+        * kernel stack.
+        */
+       testb   $SEL_RPL_MASK,TF_CS-TF_RIP(%rsp)
+       jnz     210f                            /* jnz from userland */
+
+       /*
+        * From kernel - %gs and %cr3 may be inconsistent.  Save original
+        * values and load consistent values, restore after return.
+        *
+        * The trap handler is NOT allowed to block for this case.
+        */
+       subq    $TR_RIP, %rsp
+       movq    %rax, TR_RAX(%rsp)
+       movq    %rcx, TR_RCX(%rsp)
+       movq    %rdx, TR_RDX(%rsp)
+
+       cld
+       movq    %cr3,%rax                       /* save CR3 */
+       movq    %rax, TR_PCB_CR3_SAVED(%rsp)
+       movl    $MSR_GSBASE,%ecx                /* save %gs */
+       rdmsr
+       shlq    $32,%rdx
+       orq     %rdx,%rax
+       movq    %rax, TR_PCB_GS_SAVED(%rsp)
+       movq    TR_PCB_GS_KERNEL(%rsp),%rdx     /* retrieve kernel %gs */
+       movl    %edx,%eax
+       shrq    $32,%rdx
+       wrmsr
+       movq    PCPU(trampoline)+TR_PCB_CR3,%rax
+       movq    %rax,%cr3
+
+       movq    TR_RDX(%rsp), %rdx
+       movq    TR_RCX(%rsp), %rcx
+       movq    TR_RAX(%rsp), %rax
+       addq    $TR_RIP, %rsp
+
+       /*
+        * We are coming from the kernel.
+        *
+        * We are on the IST2 stack and, in fact, we have to *STAY* on this
+        * stack so no longer try to shift our frame to the kernel %rsp
+        * in the trap frame, since this %rsp might actually be a user %rsp
+        * in the mov mem,%ss + syscall DBG trap case.
+        *
+        * Run the normal trap.  Because TF_CS is at a kernel RPL, the
+        * normal code will skip the usual swapgs and KMMU (trampoline)
+        * code.  We've handled the rest.
+        *
+        * NOTE: at this point the trampframe is above the normal stack
+        *       frame.  The trap code will be ignorant of the special
+        *       TR_* registers above the cpu hardware frame portion,
+        *       and the TR_* registers below it will be overwritten.
+        */
+       PUSH_FRAME_TFRIP
+       movq    $0,TF_XFLAGS(%rsp)
+       movq    $T_TRCTRAP,TF_TRAPNO(%rsp)
+       movq    $0,TF_ADDR(%rsp)
+       movq    $0,TF_ERR(%rsp)
+
+       FAKE_MCOUNT(TF_RIP(%rsp))
+       cld
+       movq    %rsp, %rdi
+       call    trap
+       MEXITCOUNT
+
+       /*
+        * Pop the frame (since we're coming from kernel mode, this will
+        * not mess with %cr3 or %gs), then restore %cr3 and %gs for our
+        * iretq.  Not optimal but more readable and this is not a
+        * critical path.
+        */
+       POP_FRAME(nop)
+
+       subq    $TR_RIP, %rsp
+       movq    %rax, TR_RAX(%rsp)
+       movq    %rcx, TR_RCX(%rsp)
+       movq    %rdx, TR_RDX(%rsp)
+
+       movl    $MSR_GSBASE,%ecx                /* restore %gs */
+       movq    TR_PCB_GS_SAVED(%rsp),%rdx
+       movl    %edx,%eax
+       shrq    $32,%rdx
+       wrmsr
+
+       movq    TR_PCB_CR3_SAVED(%rsp),%rax     /* restore %cr3 */
+       movq    %rax,%cr3
+
+       movq    TR_RAX(%rsp),%rax
+       movq    TR_RCX(%rsp),%rcx
+       movq    TR_RDX(%rsp),%rdx
+       addq    $TR_RIP, %rsp
+
+       /*
+        * Direct iretq. No point jumping to doreti because the
+        * exception code that deals with iretq faults can't handle
+        * non-deterministic %gs/%cr3 state.
+        */
+#ifdef DIRECT_DISALLOW_SS_CPUBUG
+200:
+#endif
+       iretq
+
+       /*
+        * From userland (normal trap path)
+        */
+210:
        TRAP(T_TRCTRAP)
+       /* NOT REACHED */
+
 IDTVEC(bpt)
        TRAP(T_BPTFLT)
 IDTVEC(div)
@@ -326,26 +471,117 @@ IDTVEC(fast_syscall32)
 /*
  * NMI handling is special.
  *
- * First, NMIs do not respect the state of the processor's RFLAGS.IF
- * bit and the NMI handler may be invoked at any time, including when
- * the processor is in a critical section with RFLAGS.IF == 0.  In
- * particular, this means that the processor's GS.base values could be
- * inconsistent on entry to the handler, and so we need to read
- * MSR_GSBASE to determine if a 'swapgs' is needed.  We use '%ebx', a
- * C-preserved register, to remember whether to swap GS back on the
- * exit path.
+ * First, an NMI is taken on its own pcpu stack.  RFLAGS.IF, %gs, and %cr3
+ * will be inconsistent when interrupt supervisor mode.
  *
  * Second, the processor treats NMIs specially, blocking further NMIs
  * until an 'iretq' instruction is executed.  We therefore need to
  * execute the NMI handler with interrupts disabled to prevent a
  * nested interrupt from executing an 'iretq' instruction and
  * inadvertently taking the processor out of NMI mode.
- *
- * Third, the NMI handler runs on its own stack (tss_ist1), shared
- * with the double fault handler.
  */
-
 IDTVEC(nmi)
+       /*
+        * We don't need to special-case entry from userland, %gs will
+        * be consistent with expectations.
+        */
+       testb   $SEL_RPL_MASK,TF_CS-TF_RIP(%rsp) ; /* from userland? */ \
+       jnz     200f
+
+       /*
+        * From kernel - %gs and %cr3 may be inconsistent.  Save original
+        * values and load consistent values, restore on return.
+        *
+        * The trap handler is NOT allowed to block for this case.
+        */
+       subq    $TR_RIP, %rsp
+       movq    %rax, TR_RAX(%rsp)
+       movq    %rcx, TR_RCX(%rsp)
+       movq    %rdx, TR_RDX(%rsp)
+
+       cld
+       movq    %cr3,%rax                       /* save CR3 */
+       movq    %rax, TR_PCB_CR3_SAVED(%rsp)
+       movl    $MSR_GSBASE,%ecx                /* save %gs */
+       rdmsr
+       shlq    $32,%rdx
+       orq     %rdx,%rax
+       movq    %rax, TR_PCB_GS_SAVED(%rsp)
+       movq    TR_PCB_GS_KERNEL(%rsp),%rdx     /* retrieve kernel %gs */
+       movl    %edx,%eax
+       shrq    $32,%rdx
+       wrmsr
+#if 0
+       movq    TR_PCB_CR3(%rsp),%rax           /* retrieve kernel %cr3 */
+#endif
+       movq    PCPU(trampoline)+TR_PCB_CR3,%rax
+       movq    %rax,%cr3
+
+       movq    TR_RDX(%rsp), %rdx
+       movq    TR_RCX(%rsp), %rcx
+       movq    TR_RAX(%rsp), %rax
+       addq    $TR_RIP, %rsp
+
+       /*
+        * Ok, run the normal trap.  Because TF_CS is at a kernel RPL,
+        * the normal code will skip the usual swapgs and KMMU (trampoline)
+        * code.  We've handled the rest.
+        *
+        * NOTE: at this point the trampframe is above the normal stack
+        *       frame.  The trap code will be ignorant of the special
+        *       TR_* registers above the cpu hardware frame portion,
+        *       and the TR_* registers below it will be overwritten.
+        */
+       PUSH_FRAME_TFRIP
+       movq    $0,TF_XFLAGS(%rsp)
+       movq    $T_NMI,TF_TRAPNO(%rsp)
+       movq    $0,TF_ADDR(%rsp)
+       movq    $0,TF_ERR(%rsp)
+
+       FAKE_MCOUNT(TF_RIP(%rsp))
+       cld
+       movq    %rsp, %rdi
+       call    trap
+       MEXITCOUNT
+
+       /*
+        * Pop the frame (since we're coming from kernel mode, this will
+        * not mess with %cr3 or %gs), then restore %cr3 and %gs for our
+        * iretq.  Not optimal but more readable and this is not a
+        * critical path.
+        */
+       POP_FRAME(nop)
+
+       subq    $TR_RIP, %rsp
+       movq    %rax, TR_RAX(%rsp)
+       movq    %rcx, TR_RCX(%rsp)
+       movq    %rdx, TR_RDX(%rsp)
+
+       movl    $MSR_GSBASE,%ecx                /* restore %gs */
+       movq    TR_PCB_GS_SAVED(%rsp),%rdx
+       movl    %edx,%eax
+       shrq    $32,%rdx
+       wrmsr
+
+       movq    TR_PCB_CR3_SAVED(%rsp),%rax     /* restore %cr3 */
+       movq    %rax,%cr3
+
+       movq    TR_RAX(%rsp),%rax
+       movq    TR_RCX(%rsp),%rcx
+       movq    TR_RDX(%rsp),%rdx
+       addq    $TR_RIP, %rsp
+
+       /*
+        * Direct iretq. No point jumping to doreti because the
+        * exception code that deals with iretq faults can't handle
+        * non-deterministic %gs/%cr3 state.
+        */
+       iretq
+
+       /*
+        * From userland (normal trap path)
+        */
+200:
        PUSH_FRAME_TFRIP
        movq    $0,TF_XFLAGS(%rsp)
        movq    $T_NMI,TF_TRAPNO(%rsp)
@@ -435,6 +671,7 @@ ENTRY(fork_trampoline)
 
        .data
        .p2align 4
+
        .text
        SUPERALIGN_TEXT
 MCOUNT_LABEL(bintr)
index cee837f..1ae1e1e 100644 (file)
@@ -228,6 +228,9 @@ ASSYM(TR_PCB_FLAGS, offsetof(struct trampframe, tr_pcb_flags));
 ASSYM(TR_PCB_CR3_ISO, offsetof(struct trampframe, tr_pcb_cr3_iso));
 ASSYM(TR_PCB_CR3, offsetof(struct trampframe, tr_pcb_cr3));
 ASSYM(TR_PCB_GFLAGS, offsetof(struct trampframe, tr_pcb_gflags));
+ASSYM(TR_PCB_GS_KERNEL, offsetof(struct trampframe, tr_pcb_gs_kernel));
+ASSYM(TR_PCB_GS_SAVED, offsetof(struct trampframe, tr_pcb_gs_saved));
+ASSYM(TR_PCB_CR3_SAVED, offsetof(struct trampframe, tr_pcb_cr3_saved));
 
 ASSYM(GD_IPENDING, offsetof(struct mdglobaldata, gd_ipending));
 ASSYM(GD_SPENDING, offsetof(struct mdglobaldata, gd_spending));
index b2c69a8..b61761f 100644 (file)
@@ -2546,7 +2546,7 @@ hammer_time(u_int64_t modulep, u_int64_t physfree)
        for (x = 0; x < NIDT; x++)
                setidt_global(x, &IDTVEC(rsvd), SDT_SYSIGT, SEL_KPL, 0);
        setidt_global(IDT_DE, &IDTVEC(div),  SDT_SYSIGT, SEL_KPL, 0);
-       setidt_global(IDT_DB, &IDTVEC(dbg),  SDT_SYSIGT, SEL_KPL, 0);
+       setidt_global(IDT_DB, &IDTVEC(dbg),  SDT_SYSIGT, SEL_KPL, 2);
        setidt_global(IDT_NMI, &IDTVEC(nmi),  SDT_SYSIGT, SEL_KPL, 1);
        setidt_global(IDT_BP, &IDTVEC(bpt),  SDT_SYSIGT, SEL_UPL, 0);
        setidt_global(IDT_OF, &IDTVEC(ofl),  SDT_SYSIGT, SEL_KPL, 0);
@@ -2669,10 +2669,17 @@ hammer_time(u_int64_t modulep, u_int64_t physfree)
         */
        ps->common_tss.tss_rsp0 = (register_t)&ps->trampoline.tr_pcb_rsp;
        ps->trampoline.tr_pcb_rsp = ps->common_tss.tss_rsp0;
+       ps->trampoline.tr_pcb_gs_kernel = (register_t)gd;
+       ps->trampoline.tr_pcb_cr3 = KPML4phys;  /* adj to user cr3 live */
+       ps->dbltramp.tr_pcb_gs_kernel = (register_t)gd;
+       ps->dbltramp.tr_pcb_cr3 = KPML4phys;
+       ps->dbgtramp.tr_pcb_gs_kernel = (register_t)gd;
+       ps->dbgtramp.tr_pcb_cr3 = KPML4phys;
 
        /* double fault stack */
-       ps->common_tss.tss_ist1 = (register_t)ps->dblstack +
-                                 sizeof(ps->dblstack);
+       ps->common_tss.tss_ist1 = (register_t)&ps->dbltramp.tr_pcb_rsp;
+       /* #DB debugger needs its own stack */
+       ps->common_tss.tss_ist2 = (register_t)&ps->dbgtramp.tr_pcb_rsp;
 
        /* Set the IO permission bitmap (empty due to tss seg limit) */
        ps->common_tss.tss_iobase = sizeof(struct x86_64tss);
@@ -3074,9 +3081,8 @@ user_dbreg_trap(void)
                 addr[nbp++] = (caddr_t)rdr3();
         }
 
-        for (i=0; i<nbp; i++) {
-                if (addr[i] <
-                    (caddr_t)VM_MAX_USER_ADDRESS) {
+        for (i = 0; i < nbp; i++) {
+                if (addr[i] < (caddr_t)VM_MAX_USER_ADDRESS) {
                         /*
                          * addr[i] is in user space
                          */
index adba3ec..993e76b 100644 (file)
@@ -297,6 +297,12 @@ init_secondary(void)
         */
        ps->common_tss.tss_rsp0 = (register_t)&ps->trampoline.tr_pcb_rsp;
        ps->trampoline.tr_pcb_rsp = ps->common_tss.tss_rsp0;
+       ps->trampoline.tr_pcb_gs_kernel = (register_t)md;
+       ps->trampoline.tr_pcb_cr3 = KPML4phys;  /* adj to user cr3 live */
+       ps->dbltramp.tr_pcb_gs_kernel = (register_t)md;
+       ps->dbltramp.tr_pcb_cr3 = KPML4phys;
+       ps->dbgtramp.tr_pcb_gs_kernel = (register_t)md;
+       ps->dbgtramp.tr_pcb_cr3 = KPML4phys;
 
 #if 0 /* JG XXX */
        ps->common_tss.tss_ioopt = (sizeof ps->common_tss) << 16;
@@ -305,8 +311,8 @@ init_secondary(void)
        md->gd_common_tssd = *md->gd_tss_gdt;
 
        /* double fault stack */
-       ps->common_tss.tss_ist1 = (register_t)ps->dblstack +
-                                 sizeof(ps->dblstack);
+       ps->common_tss.tss_ist1 = (register_t)&ps->dbltramp.tr_pcb_rsp;
+       ps->common_tss.tss_ist2 = (register_t)&ps->dbgtramp.tr_pcb_rsp;
 
        ltr(gsel_tss);
 
index e4c69cf..c464cd1 100644 (file)
@@ -1405,6 +1405,10 @@ pmap_init2_iso_pmap(void)
                ps = CPU_prvspace[n];
                pmap_init_iso_range((vm_offset_t)&ps->trampoline,
                                    sizeof(ps->trampoline));
+               pmap_init_iso_range((vm_offset_t)&ps->dblstack,
+                                   sizeof(ps->dblstack));
+               pmap_init_iso_range((vm_offset_t)&ps->dbgstack,
+                                   sizeof(ps->dbgstack));
                pmap_init_iso_range((vm_offset_t)&ps->common_tss,
                                    sizeof(ps->common_tss));
                pmap_init_iso_range(r_idt_arr[n].rd_base,
index e4505d8..3016250 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 1990, 1993
  *     The Regents of the University of California.  All rights reserved.
  * Copyright (C) 1994, David Greenman
- * Copyright (c) 2008 The DragonFly Project.
+ * Copyright (c) 2008-2018 The DragonFly Project.
  * Copyright (c) 2008 Jordan Gordeev.
  *
  * This code is derived from software contributed to Berkeley by
 #include <sys/thread2.h>
 #include <sys/spinlock2.h>
 
+/*
+ * These %rip's are used to detect a historical CPU artifact on syscall or
+ * int $3 entry, if not shortcutted in exception.S via
+ * DIRECT_DISALLOW_SS_CPUBUG.
+ */
+extern void Xbpt(void);
+extern void Xfast_syscall(void);
+#define IDTVEC(vec)    X##vec
+
 extern void trap(struct trapframe *frame);
 
 static int trap_pfault(struct trapframe *, int);
@@ -375,12 +384,17 @@ KTR_INFO(KTR_KERNENTRY, kernentry, fork_ret, 0, "FORKRET(pid %d, tid %d)",
  * NOTE!  We have to retrieve the fault address prior to potentially
  *       blocking, including blocking on any token.
  *
+ * NOTE!  NMI and kernel DBG traps remain on their respective pcpu IST
+ *       stacks if taken from a kernel RPL. trap() cannot block in this
+ *       situation.  DDB entry or a direct report-and-return is ok.
+ *
  * XXX gd_trap_nesting_level currently prevents lwkt_switch() from panicing
  * if an attempt is made to switch from a fast interrupt or IPI.
  */
 void
 trap(struct trapframe *frame)
 {
+       static struct krate sscpubugrate = { 1 };
        struct globaldata *gd = mycpu;
        struct thread *td = gd->gd_curthread;
        struct lwp *lp = td->td_lwp;
@@ -686,25 +700,21 @@ trap(struct trapframe *frame)
                        break;
 
                case T_TRCTRAP:  /* trace trap */
-#if 0
-                       if (frame->tf_rip == (int)IDTVEC(syscall)) {
-                               /*
-                                * We've just entered system mode via the
-                                * syscall lcall.  Continue single stepping
-                                * silently until the syscall handler has
-                                * saved the flags.
-                                */
+                       /*
+                        * Detect historical CPU artifact on syscall or int $3
+                        * entry (if not shortcutted in exception.s via
+                        * DIRECT_DISALLOW_SS_CPUBUG).
+                        */
+                       if (frame->tf_rip == (register_t)IDTVEC(fast_syscall)) {
+                               krateprintf(&sscpubugrate,
+                                       "Caught #DB at syscall cpu artifact\n");
                                goto out2;
                        }
-                       if (frame->tf_rip == (int)IDTVEC(syscall) + 1) {
-                               /*
-                                * The syscall handler has now saved the
-                                * flags.  Stop single stepping it.
-                                */
-                               frame->tf_rflags &= ~PSL_T;
+                       if (frame->tf_rip == (register_t)IDTVEC(bpt)) {
+                               krateprintf(&sscpubugrate,
+                                       "Caught #DB at int $N cpu artifact\n");
                                goto out2;
                        }
-#endif
 
                        /*
                         * Ignore debug register trace traps due to
@@ -716,17 +726,14 @@ trap(struct trapframe *frame)
                         * in kernel space because that is useful when
                         * debugging the kernel.
                         */
-#if 0 /* JG */
                        if (user_dbreg_trap()) {
                                /*
                                 * Reset breakpoint bits because the
                                 * processor doesn't
                                 */
-                               /* XXX check upper bits here */
-                               load_dr6(rdr6() & 0xfffffff0);
+                               load_dr6(rdr6() & ~0xf);
                                goto out2;
                        }
-#endif
                        /*
                         * FALLTHROUGH (TRCTRAP kernel mode, kernel address)
                         */