Skip to content

Commit d5c678a

Browse files
amlutoKAGA-KOKO
authored andcommitted
x86/debug: Allow a single level of #DB recursion
Trying to clear DR7 around a #DB from usermode malfunctions if the tasks schedules when delivering SIGTRAP. Rather than trying to define a special no-recursion region, just allow a single level of recursion. The same mechanism is used for NMI, and it hasn't caused any problems yet. Fixes: 9f58fdd ("x86/db: Split out dr6/7 handling") Reported-by: Kyle Huey <[email protected]> Debugged-by: Josh Poimboeuf <[email protected]> Signed-off-by: Andy Lutomirski <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Daniel Thompson <[email protected]> Cc: [email protected] Link: https://lkml.kernel.org/r/8b9bd05f187231df008d48cf818a6a311cbd5c98.1597882384.git.luto@kernel.org Link: https://lore.kernel.org/r/[email protected]
1 parent 662a022 commit d5c678a

File tree

1 file changed

+31
-34
lines changed

1 file changed

+31
-34
lines changed

arch/x86/kernel/traps.c

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -729,20 +729,9 @@ static bool is_sysenter_singlestep(struct pt_regs *regs)
729729
#endif
730730
}
731731

732-
static __always_inline void debug_enter(unsigned long *dr6, unsigned long *dr7)
732+
static __always_inline unsigned long debug_read_clear_dr6(void)
733733
{
734-
/*
735-
* Disable breakpoints during exception handling; recursive exceptions
736-
* are exceedingly 'fun'.
737-
*
738-
* Since this function is NOKPROBE, and that also applies to
739-
* HW_BREAKPOINT_X, we can't hit a breakpoint before this (XXX except a
740-
* HW_BREAKPOINT_W on our stack)
741-
*
742-
* Entry text is excluded for HW_BP_X and cpu_entry_area, which
743-
* includes the entry stack is excluded for everything.
744-
*/
745-
*dr7 = local_db_save();
734+
unsigned long dr6;
746735

747736
/*
748737
* The Intel SDM says:
@@ -755,15 +744,12 @@ static __always_inline void debug_enter(unsigned long *dr6, unsigned long *dr7)
755744
*
756745
* Keep it simple: clear DR6 immediately.
757746
*/
758-
get_debugreg(*dr6, 6);
747+
get_debugreg(dr6, 6);
759748
set_debugreg(0, 6);
760749
/* Filter out all the reserved bits which are preset to 1 */
761-
*dr6 &= ~DR6_RESERVED;
762-
}
750+
dr6 &= ~DR6_RESERVED;
763751

764-
static __always_inline void debug_exit(unsigned long dr7)
765-
{
766-
local_db_restore(dr7);
752+
return dr6;
767753
}
768754

769755
/*
@@ -863,6 +849,18 @@ static void handle_debug(struct pt_regs *regs, unsigned long dr6, bool user)
863849
static __always_inline void exc_debug_kernel(struct pt_regs *regs,
864850
unsigned long dr6)
865851
{
852+
/*
853+
* Disable breakpoints during exception handling; recursive exceptions
854+
* are exceedingly 'fun'.
855+
*
856+
* Since this function is NOKPROBE, and that also applies to
857+
* HW_BREAKPOINT_X, we can't hit a breakpoint before this (XXX except a
858+
* HW_BREAKPOINT_W on our stack)
859+
*
860+
* Entry text is excluded for HW_BP_X and cpu_entry_area, which
861+
* includes the entry stack is excluded for everything.
862+
*/
863+
unsigned long dr7 = local_db_save();
866864
bool irq_state = idtentry_enter_nmi(regs);
867865
instrumentation_begin();
868866

@@ -883,6 +881,8 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
883881

884882
instrumentation_end();
885883
idtentry_exit_nmi(regs, irq_state);
884+
885+
local_db_restore(dr7);
886886
}
887887

888888
static __always_inline void exc_debug_user(struct pt_regs *regs,
@@ -894,6 +894,15 @@ static __always_inline void exc_debug_user(struct pt_regs *regs,
894894
*/
895895
WARN_ON_ONCE(!user_mode(regs));
896896

897+
/*
898+
* NB: We can't easily clear DR7 here because
899+
* idtentry_exit_to_usermode() can invoke ptrace, schedule, access
900+
* user memory, etc. This means that a recursive #DB is possible. If
901+
* this happens, that #DB will hit exc_debug_kernel() and clear DR7.
902+
* Since we're not on the IST stack right now, everything will be
903+
* fine.
904+
*/
905+
897906
irqentry_enter_from_user_mode(regs);
898907
instrumentation_begin();
899908

@@ -907,36 +916,24 @@ static __always_inline void exc_debug_user(struct pt_regs *regs,
907916
/* IST stack entry */
908917
DEFINE_IDTENTRY_DEBUG(exc_debug)
909918
{
910-
unsigned long dr6, dr7;
911-
912-
debug_enter(&dr6, &dr7);
913-
exc_debug_kernel(regs, dr6);
914-
debug_exit(dr7);
919+
exc_debug_kernel(regs, debug_read_clear_dr6());
915920
}
916921

917922
/* User entry, runs on regular task stack */
918923
DEFINE_IDTENTRY_DEBUG_USER(exc_debug)
919924
{
920-
unsigned long dr6, dr7;
921-
922-
debug_enter(&dr6, &dr7);
923-
exc_debug_user(regs, dr6);
924-
debug_exit(dr7);
925+
exc_debug_user(regs, debug_read_clear_dr6());
925926
}
926927
#else
927928
/* 32 bit does not have separate entry points. */
928929
DEFINE_IDTENTRY_RAW(exc_debug)
929930
{
930-
unsigned long dr6, dr7;
931-
932-
debug_enter(&dr6, &dr7);
931+
unsigned long dr6 = debug_read_clear_dr6();
933932

934933
if (user_mode(regs))
935934
exc_debug_user(regs, dr6);
936935
else
937936
exc_debug_kernel(regs, dr6);
938-
939-
debug_exit(dr7);
940937
}
941938
#endif
942939

0 commit comments

Comments
 (0)