Print this page
9685 KPTI %cr3 handling needs fixes
*** 450,461 ****
popq %rdi /* the cpusave area */
movq %rsp, KRS_GREGS(%rdi) /* save ptr to current saved regs */
pushq %rdi
call kdi_trap_pass
! cmpq $1, %rax
! je kdi_pass_to_kernel
popq %rax /* cpusave in %rax */
SAVE_IDTGDT
#if !defined(__xpv)
--- 450,461 ----
popq %rdi /* the cpusave area */
movq %rsp, KRS_GREGS(%rdi) /* save ptr to current saved regs */
pushq %rdi
call kdi_trap_pass
! testq %rax, %rax
! jnz kdi_pass_to_kernel
popq %rax /* cpusave in %rax */
SAVE_IDTGDT
#if !defined(__xpv)
*** 567,603 ****
IRET
#endif
/*NOTREACHED*/
SET_SIZE(kdi_resume)
- ENTRY_NP(kdi_pass_to_kernel)
- popq %rdi /* cpusave */
-
- movq $KDI_CPU_STATE_NONE, KRS_CPU_STATE(%rdi)
-
/*
! * Find the trap and vector off the right kernel handler. The trap
! * handler will expect the stack to be in trap order, with %rip being
! * the last entry, so we'll need to restore all our regs. On i86xpv
! * we'll need to compensate for XPV_TRAP_POP.
*
* We're hard-coding the three cases where KMDB has installed permanent
* handlers, since after we KDI_RESTORE_REGS(), we don't have registers
* to work with; we can't use a global since other CPUs can easily pass
* through here at the same time.
*
* Note that we handle T_DBGENTR since userspace might have tried it.
*/
movq KRS_GREGS(%rdi), %rsp
movq REG_OFF(KDIREG_TRAPNO)(%rsp), %rdi
cmpq $T_SGLSTP, %rdi
! je 1f
cmpq $T_BPTFLT, %rdi
! je 2f
cmpq $T_DBGENTR, %rdi
! je 3f
/*
* Hmm, unknown handler. Somebody forgot to update this when they
* added a new trap interposition... try to drop back into kmdb.
*/
int $T_DBGENTR
--- 567,633 ----
IRET
#endif
/*NOTREACHED*/
SET_SIZE(kdi_resume)
/*
! * We took a trap that should be handled by the kernel, not KMDB.
*
* We're hard-coding the three cases where KMDB has installed permanent
* handlers, since after we KDI_RESTORE_REGS(), we don't have registers
* to work with; we can't use a global since other CPUs can easily pass
* through here at the same time.
*
* Note that we handle T_DBGENTR since userspace might have tried it.
+ *
+ * The trap handler will expect the stack to be in trap order, with %rip
+ * being the last entry, so we'll need to restore all our regs. On
+ * i86xpv we'll need to compensate for XPV_TRAP_POP.
+ *
+ * %rax on entry is either 1 or 2, which is from kdi_trap_pass().
+ * kdi_cmnint stashed the original %cr3 into KDIREG_CR3, then (probably)
+ * switched us to the CPU's kf_kernel_cr3. But we're about to call, for
+ * example:
+ *
+ * dbgtrap->trap()->tr_iret_kernel
+ *
+ * which, unlike, tr_iret_kdi, doesn't restore the original %cr3, so
+ * we'll do so here if needed.
+ *
+ * This isn't just a matter of tidiness: for example, consider:
+ *
+ * hat_switch(oldhat=kas.a_hat, newhat=prochat)
+ * setcr3()
+ * reset_kpti()
+ * *brktrap* due to fbt on reset_kpti:entry
+ *
+ * Here, we have the new hat's %cr3, but we haven't yet updated
+ * kf_kernel_cr3 (so its currently kas's). So if we don't restore here,
+ * we'll stay on kas's cr3 value on returning from the trap: not good if
+ * we fault on a userspace address.
*/
+ ENTRY_NP(kdi_pass_to_kernel)
+
+ popq %rdi /* cpusave */
+ movq $KDI_CPU_STATE_NONE, KRS_CPU_STATE(%rdi)
movq KRS_GREGS(%rdi), %rsp
+
+ cmpq $2, %rax
+ jne no_restore_cr3
+ movq REG_OFF(KDIREG_CR3)(%rsp), %r11
+ movq %r11, %cr3
+
+ no_restore_cr3:
movq REG_OFF(KDIREG_TRAPNO)(%rsp), %rdi
+
cmpq $T_SGLSTP, %rdi
! je kdi_pass_dbgtrap
cmpq $T_BPTFLT, %rdi
! je kdi_pass_brktrap
cmpq $T_DBGENTR, %rdi
! je kdi_pass_invaltrap
/*
* Hmm, unknown handler. Somebody forgot to update this when they
* added a new trap interposition... try to drop back into kmdb.
*/
int $T_DBGENTR
*** 607,623 ****
/* Discard state, trapno, err */ \
addq $REG_OFF(KDIREG_RIP), %rsp; \
XPV_TRAP_PUSH; \
jmp %cs:name
! 1:
CALL_TRAP_HANDLER(dbgtrap)
/*NOTREACHED*/
! 2:
CALL_TRAP_HANDLER(brktrap)
/*NOTREACHED*/
! 3:
CALL_TRAP_HANDLER(invaltrap)
/*NOTREACHED*/
SET_SIZE(kdi_pass_to_kernel)
--- 637,653 ----
/* Discard state, trapno, err */ \
addq $REG_OFF(KDIREG_RIP), %rsp; \
XPV_TRAP_PUSH; \
jmp %cs:name
! kdi_pass_dbgtrap:
CALL_TRAP_HANDLER(dbgtrap)
/*NOTREACHED*/
! kdi_pass_brktrap:
CALL_TRAP_HANDLER(brktrap)
/*NOTREACHED*/
! kdi_pass_invaltrap:
CALL_TRAP_HANDLER(invaltrap)
/*NOTREACHED*/
SET_SIZE(kdi_pass_to_kernel)