Skip to content

Commit 13cb734

Browse files
committed
Merge tag 'x86-entry-2020-10-12' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 entry code updates from Thomas Gleixner: "More consolidation and correctness fixes for the debug exception: - Ensure BTF synchronization under all circumstances - Distangle kernel and user mode #DB further - Get ordering vs. the debug notifier correct to make KGDB work more reliably. - Cleanup historical gunk and make the code simpler to understand" * tag 'x86-entry-2020-10-12' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/debug: Change thread.debugreg6 to thread.virtual_dr6 x86/debug: Support negative polarity DR6 bits x86/debug: Simplify hw_breakpoint_handler() x86/debug: Remove aout_dump_debugregs() x86/debug: Remove the historical junk x86/debug: Move cond_local_irq_enable() block into exc_debug_user() x86/debug: Move historical SYSENTER junk into exc_debug_kernel() x86/debug: Simplify #DB signal code x86/debug: Remove handle_debug(.user) argument x86/debug: Move kprobe_debug_handler() into exc_debug_kernel() x86/debug: Sync BTF earlier
2 parents cc73437 + d53d9bc commit 13cb734

7 files changed

Lines changed: 94 additions & 129 deletions

File tree

arch/x86/include/asm/debugreg.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ static __always_inline bool hw_breakpoint_active(void)
9090
return __this_cpu_read(cpu_dr7) & DR_GLOBAL_ENABLE_MASK;
9191
}
9292

93-
extern void aout_dump_debugregs(struct user *dump);
94-
9593
extern void hw_breakpoint_restore(void);
9694

9795
static __always_inline unsigned long local_db_save(void)

arch/x86/include/asm/kprobes.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,9 @@ extern int kprobe_exceptions_notify(struct notifier_block *self,
106106
extern int kprobe_int3_handler(struct pt_regs *regs);
107107
extern int kprobe_debug_handler(struct pt_regs *regs);
108108

109+
#else
110+
111+
static inline int kprobe_debug_handler(struct pt_regs *regs) { return 0; }
112+
109113
#endif /* CONFIG_KPROBES */
110114
#endif /* _ASM_X86_KPROBES_H */

arch/x86/include/asm/processor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ struct thread_struct {
517517
/* Save middle states of ptrace breakpoints */
518518
struct perf_event *ptrace_bps[HBP_NUM];
519519
/* Debug status used for traps, single steps, etc... */
520-
unsigned long debugreg6;
520+
unsigned long virtual_dr6;
521521
/* Keep track of the exact dr7 value set by the user */
522522
unsigned long ptrace_dr7;
523523
/* Fault info: */

arch/x86/kernel/hw_breakpoint.c

Lines changed: 6 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -441,42 +441,6 @@ int hw_breakpoint_arch_parse(struct perf_event *bp,
441441
return 0;
442442
}
443443

444-
/*
445-
* Dump the debug register contents to the user.
446-
* We can't dump our per cpu values because it
447-
* may contain cpu wide breakpoint, something that
448-
* doesn't belong to the current task.
449-
*
450-
* TODO: include non-ptrace user breakpoints (perf)
451-
*/
452-
void aout_dump_debugregs(struct user *dump)
453-
{
454-
int i;
455-
int dr7 = 0;
456-
struct perf_event *bp;
457-
struct arch_hw_breakpoint *info;
458-
struct thread_struct *thread = &current->thread;
459-
460-
for (i = 0; i < HBP_NUM; i++) {
461-
bp = thread->ptrace_bps[i];
462-
463-
if (bp && !bp->attr.disabled) {
464-
dump->u_debugreg[i] = bp->attr.bp_addr;
465-
info = counter_arch_bp(bp);
466-
dr7 |= encode_dr7(i, info->len, info->type);
467-
} else {
468-
dump->u_debugreg[i] = 0;
469-
}
470-
}
471-
472-
dump->u_debugreg[4] = 0;
473-
dump->u_debugreg[5] = 0;
474-
dump->u_debugreg[6] = current->thread.debugreg6;
475-
476-
dump->u_debugreg[7] = dr7;
477-
}
478-
EXPORT_SYMBOL_GPL(aout_dump_debugregs);
479-
480444
/*
481445
* Release the user breakpoints used by ptrace
482446
*/
@@ -490,7 +454,7 @@ void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
490454
t->ptrace_bps[i] = NULL;
491455
}
492456

493-
t->debugreg6 = 0;
457+
t->virtual_dr6 = 0;
494458
t->ptrace_dr7 = 0;
495459
}
496460

@@ -500,7 +464,7 @@ void hw_breakpoint_restore(void)
500464
set_debugreg(__this_cpu_read(cpu_debugreg[1]), 1);
501465
set_debugreg(__this_cpu_read(cpu_debugreg[2]), 2);
502466
set_debugreg(__this_cpu_read(cpu_debugreg[3]), 3);
503-
set_debugreg(current->thread.debugreg6, 6);
467+
set_debugreg(DR6_RESERVED, 6);
504468
set_debugreg(__this_cpu_read(cpu_dr7), 7);
505469
}
506470
EXPORT_SYMBOL_GPL(hw_breakpoint_restore);
@@ -523,10 +487,10 @@ EXPORT_SYMBOL_GPL(hw_breakpoint_restore);
523487
*/
524488
static int hw_breakpoint_handler(struct die_args *args)
525489
{
526-
int i, cpu, rc = NOTIFY_STOP;
490+
int i, rc = NOTIFY_STOP;
527491
struct perf_event *bp;
528-
unsigned long dr6;
529492
unsigned long *dr6_p;
493+
unsigned long dr6;
530494

531495
/* The DR6 value is pointed by args->err */
532496
dr6_p = (unsigned long *)ERR_PTR(args->err);
@@ -540,14 +504,6 @@ static int hw_breakpoint_handler(struct die_args *args)
540504
if ((dr6 & DR_TRAP_BITS) == 0)
541505
return NOTIFY_DONE;
542506

543-
/*
544-
* Assert that local interrupts are disabled
545-
* Reset the DRn bits in the virtualized register value.
546-
* The ptrace trigger routine will add in whatever is needed.
547-
*/
548-
current->thread.debugreg6 &= ~DR_TRAP_BITS;
549-
cpu = get_cpu();
550-
551507
/* Handle all the breakpoints that were triggered */
552508
for (i = 0; i < HBP_NUM; ++i) {
553509
if (likely(!(dr6 & (DR_TRAP0 << i))))
@@ -561,7 +517,7 @@ static int hw_breakpoint_handler(struct die_args *args)
561517
*/
562518
rcu_read_lock();
563519

564-
bp = per_cpu(bp_per_reg[i], cpu);
520+
bp = this_cpu_read(bp_per_reg[i]);
565521
/*
566522
* Reset the 'i'th TRAP bit in dr6 to denote completion of
567523
* exception handling
@@ -592,12 +548,10 @@ static int hw_breakpoint_handler(struct die_args *args)
592548
* breakpoints (to generate signals) and b) when the system has
593549
* taken exception due to multiple causes
594550
*/
595-
if ((current->thread.debugreg6 & DR_TRAP_BITS) ||
551+
if ((current->thread.virtual_dr6 & DR_TRAP_BITS) ||
596552
(dr6 & (~DR_TRAP_BITS)))
597553
rc = NOTIFY_DONE;
598554

599-
put_cpu();
600-
601555
return rc;
602556
}
603557

arch/x86/kernel/kgdb.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -629,9 +629,10 @@ static void kgdb_hw_overflow_handler(struct perf_event *event,
629629
struct task_struct *tsk = current;
630630
int i;
631631

632-
for (i = 0; i < 4; i++)
632+
for (i = 0; i < 4; i++) {
633633
if (breakinfo[i].enabled)
634-
tsk->thread.debugreg6 |= (DR_TRAP0 << i);
634+
tsk->thread.virtual_dr6 |= (DR_TRAP0 << i);
635+
}
635636
}
636637

637638
void kgdb_arch_late(void)

arch/x86/kernel/ptrace.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ static void ptrace_triggered(struct perf_event *bp,
465465
break;
466466
}
467467

468-
thread->debugreg6 |= (DR_TRAP0 << i);
468+
thread->virtual_dr6 |= (DR_TRAP0 << i);
469469
}
470470

471471
/*
@@ -601,7 +601,7 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
601601
if (bp)
602602
val = bp->hw.info.address;
603603
} else if (n == 6) {
604-
val = thread->debugreg6;
604+
val = thread->virtual_dr6 ^ DR6_RESERVED; /* Flip back to arch polarity */
605605
} else if (n == 7) {
606606
val = thread->ptrace_dr7;
607607
}
@@ -657,7 +657,7 @@ static int ptrace_set_debugreg(struct task_struct *tsk, int n,
657657
if (n < HBP_NUM) {
658658
rc = ptrace_set_breakpoint_addr(tsk, n, val);
659659
} else if (n == 6) {
660-
thread->debugreg6 = val;
660+
thread->virtual_dr6 = val ^ DR6_RESERVED; /* Flip to positive polarity */
661661
rc = 0;
662662
} else if (n == 7) {
663663
rc = ptrace_write_dr7(tsk, val);

arch/x86/kernel/traps.c

Lines changed: 77 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -745,9 +745,21 @@ static __always_inline unsigned long debug_read_clear_dr6(void)
745745
* Keep it simple: clear DR6 immediately.
746746
*/
747747
get_debugreg(dr6, 6);
748-
set_debugreg(0, 6);
749-
/* Filter out all the reserved bits which are preset to 1 */
750-
dr6 &= ~DR6_RESERVED;
748+
set_debugreg(DR6_RESERVED, 6);
749+
dr6 ^= DR6_RESERVED; /* Flip to positive polarity */
750+
751+
/*
752+
* Clear the virtual DR6 value, ptrace routines will set bits here for
753+
* things we want signals for.
754+
*/
755+
current->thread.virtual_dr6 = 0;
756+
757+
/*
758+
* The SDM says "The processor clears the BTF flag when it
759+
* generates a debug exception." Clear TIF_BLOCKSTEP to keep
760+
* TIF_BLOCKSTEP in sync with the hardware BTF flag.
761+
*/
762+
clear_thread_flag(TIF_BLOCKSTEP);
751763

752764
return dr6;
753765
}
@@ -776,74 +788,20 @@ static __always_inline unsigned long debug_read_clear_dr6(void)
776788
*
777789
* May run on IST stack.
778790
*/
779-
static void handle_debug(struct pt_regs *regs, unsigned long dr6, bool user)
780-
{
781-
struct task_struct *tsk = current;
782-
bool user_icebp;
783-
int si_code;
784-
785-
/*
786-
* The SDM says "The processor clears the BTF flag when it
787-
* generates a debug exception." Clear TIF_BLOCKSTEP to keep
788-
* TIF_BLOCKSTEP in sync with the hardware BTF flag.
789-
*/
790-
clear_thread_flag(TIF_BLOCKSTEP);
791-
792-
/*
793-
* If DR6 is zero, no point in trying to handle it. The kernel is
794-
* not using INT1.
795-
*/
796-
if (!user && !dr6)
797-
return;
798791

792+
static bool notify_debug(struct pt_regs *regs, unsigned long *dr6)
793+
{
799794
/*
800-
* If dr6 has no reason to give us about the origin of this trap,
801-
* then it's very likely the result of an icebp/int01 trap.
802-
* User wants a sigtrap for that.
795+
* Notifiers will clear bits in @dr6 to indicate the event has been
796+
* consumed - hw_breakpoint_handler(), single_stop_cont().
797+
*
798+
* Notifiers will set bits in @virtual_dr6 to indicate the desire
799+
* for signals - ptrace_triggered(), kgdb_hw_overflow_handler().
803800
*/
804-
user_icebp = user && !dr6;
805-
806-
/* Store the virtualized DR6 value */
807-
tsk->thread.debugreg6 = dr6;
808-
809-
#ifdef CONFIG_KPROBES
810-
if (kprobe_debug_handler(regs)) {
811-
return;
812-
}
813-
#endif
814-
815-
if (notify_die(DIE_DEBUG, "debug", regs, (long)&dr6, 0,
816-
SIGTRAP) == NOTIFY_STOP) {
817-
return;
818-
}
819-
820-
/* It's safe to allow irq's after DR6 has been saved */
821-
cond_local_irq_enable(regs);
822-
823-
if (v8086_mode(regs)) {
824-
handle_vm86_trap((struct kernel_vm86_regs *) regs, 0,
825-
X86_TRAP_DB);
826-
goto out;
827-
}
828-
829-
if (WARN_ON_ONCE((dr6 & DR_STEP) && !user_mode(regs))) {
830-
/*
831-
* Historical junk that used to handle SYSENTER single-stepping.
832-
* This should be unreachable now. If we survive for a while
833-
* without anyone hitting this warning, we'll turn this into
834-
* an oops.
835-
*/
836-
tsk->thread.debugreg6 &= ~DR_STEP;
837-
set_tsk_thread_flag(tsk, TIF_SINGLESTEP);
838-
regs->flags &= ~X86_EFLAGS_TF;
839-
}
840-
841-
si_code = get_si_code(tsk->thread.debugreg6);
842-
if (tsk->thread.debugreg6 & (DR_STEP | DR_TRAP_BITS) || user_icebp)
843-
send_sigtrap(regs, 0, si_code);
801+
if (notify_die(DIE_DEBUG, "debug", regs, (long)dr6, 0, SIGTRAP) == NOTIFY_STOP)
802+
return true;
844803

845-
out:
846-
cond_local_irq_disable(regs);
804+
return false;
847805
}
848806

849807
static __always_inline void exc_debug_kernel(struct pt_regs *regs,
@@ -877,8 +835,32 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
877835
if ((dr6 & DR_STEP) && is_sysenter_singlestep(regs))
878836
dr6 &= ~DR_STEP;
879837

880-
handle_debug(regs, dr6, false);
838+
if (kprobe_debug_handler(regs))
839+
goto out;
840+
841+
/*
842+
* The kernel doesn't use INT1
843+
*/
844+
if (!dr6)
845+
goto out;
881846

847+
if (notify_debug(regs, &dr6))
848+
goto out;
849+
850+
/*
851+
* The kernel doesn't use TF single-step outside of:
852+
*
853+
* - Kprobes, consumed through kprobe_debug_handler()
854+
* - KGDB, consumed through notify_debug()
855+
*
856+
* So if we get here with DR_STEP set, something is wonky.
857+
*
858+
* A known way to trigger this is through QEMU's GDB stub,
859+
* which leaks #DB into the guest and causes IST recursion.
860+
*/
861+
if (WARN_ON_ONCE(dr6 & DR_STEP))
862+
regs->flags &= ~X86_EFLAGS_TF;
863+
out:
882864
instrumentation_end();
883865
idtentry_exit_nmi(regs, irq_state);
884866

@@ -888,6 +870,8 @@ static __always_inline void exc_debug_kernel(struct pt_regs *regs,
888870
static __always_inline void exc_debug_user(struct pt_regs *regs,
889871
unsigned long dr6)
890872
{
873+
bool icebp;
874+
891875
/*
892876
* If something gets miswired and we end up here for a kernel mode
893877
* #DB, we will malfunction.
@@ -906,8 +890,32 @@ static __always_inline void exc_debug_user(struct pt_regs *regs,
906890
irqentry_enter_from_user_mode(regs);
907891
instrumentation_begin();
908892

909-
handle_debug(regs, dr6, true);
893+
/*
894+
* If dr6 has no reason to give us about the origin of this trap,
895+
* then it's very likely the result of an icebp/int01 trap.
896+
* User wants a sigtrap for that.
897+
*/
898+
icebp = !dr6;
910899

900+
if (notify_debug(regs, &dr6))
901+
goto out;
902+
903+
/* It's safe to allow irq's after DR6 has been saved */
904+
local_irq_enable();
905+
906+
if (v8086_mode(regs)) {
907+
handle_vm86_trap((struct kernel_vm86_regs *)regs, 0, X86_TRAP_DB);
908+
goto out_irq;
909+
}
910+
911+
/* Add the virtual_dr6 bits for signals. */
912+
dr6 |= current->thread.virtual_dr6;
913+
if (dr6 & (DR_STEP | DR_TRAP_BITS) || icebp)
914+
send_sigtrap(regs, 0, get_si_code(dr6));
915+
916+
out_irq:
917+
local_irq_disable();
918+
out:
911919
instrumentation_end();
912920
irqentry_exit_to_user_mode(regs);
913921
}

0 commit comments

Comments
 (0)