Commit 9ad22e16 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Borislav Petkov

x86/debug: Fix DR6 handling

Tom reported that one of the GDB test-cases failed, and Boris bisected
it to commit:

  d53d9bc0 ("x86/debug: Change thread.debugreg6 to thread.virtual_dr6")

The debugging session led us to commit:

  6c0aca28 ("x86: Ignore trap bits on single step exceptions")

It turns out that TF and data breakpoints are both traps and will be
merged, while instruction breakpoints are faults and will not be merged.
This means 6c0aca28 is wrong, only TF and instruction breakpoints
need to be excluded while TF and data breakpoints can be merged.

 [ bp: Massage commit message. ]

Fixes: d53d9bc0 ("x86/debug: Change thread.debugreg6 to thread.virtual_dr6")
Fixes: 6c0aca28 ("x86: Ignore trap bits on single step exceptions")
Reported-by: default avatarTom de Vries <tdevries@suse.de>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
Cc: <stable@vger.kernel.org>
Link: https://lkml.kernel.org/r/YBMAbQGACujjfz%2Bi@hirez.programming.kicks-ass.net
Link: https://lkml.kernel.org/r/20210128211627.GB4348@worktop.programming.kicks-ass.net
parent 20bf2b37
...@@ -491,15 +491,12 @@ static int hw_breakpoint_handler(struct die_args *args) ...@@ -491,15 +491,12 @@ static int hw_breakpoint_handler(struct die_args *args)
struct perf_event *bp; struct perf_event *bp;
unsigned long *dr6_p; unsigned long *dr6_p;
unsigned long dr6; unsigned long dr6;
bool bpx;
/* The DR6 value is pointed by args->err */ /* The DR6 value is pointed by args->err */
dr6_p = (unsigned long *)ERR_PTR(args->err); dr6_p = (unsigned long *)ERR_PTR(args->err);
dr6 = *dr6_p; dr6 = *dr6_p;
/* If it's a single step, TRAP bits are random */
if (dr6 & DR_STEP)
return NOTIFY_DONE;
/* Do an early return if no trap bits are set in DR6 */ /* Do an early return if no trap bits are set in DR6 */
if ((dr6 & DR_TRAP_BITS) == 0) if ((dr6 & DR_TRAP_BITS) == 0)
return NOTIFY_DONE; return NOTIFY_DONE;
...@@ -509,28 +506,29 @@ static int hw_breakpoint_handler(struct die_args *args) ...@@ -509,28 +506,29 @@ static int hw_breakpoint_handler(struct die_args *args)
if (likely(!(dr6 & (DR_TRAP0 << i)))) if (likely(!(dr6 & (DR_TRAP0 << i))))
continue; continue;
bp = this_cpu_read(bp_per_reg[i]);
if (!bp)
continue;
bpx = bp->hw.info.type == X86_BREAKPOINT_EXECUTE;
/* /*
* The counter may be concurrently released but that can only * TF and data breakpoints are traps and can be merged, however
* occur from a call_rcu() path. We can then safely fetch * instruction breakpoints are faults and will be raised
* the breakpoint, use its callback, touch its counter * separately.
* while we are in an rcu_read_lock() path. *
* However DR6 can indicate both TF and instruction
* breakpoints. In that case take TF as that has precedence and
* delay the instruction breakpoint for the next exception.
*/ */
rcu_read_lock(); if (bpx && (dr6 & DR_STEP))
continue;
bp = this_cpu_read(bp_per_reg[i]);
/* /*
* Reset the 'i'th TRAP bit in dr6 to denote completion of * Reset the 'i'th TRAP bit in dr6 to denote completion of
* exception handling * exception handling
*/ */
(*dr6_p) &= ~(DR_TRAP0 << i); (*dr6_p) &= ~(DR_TRAP0 << i);
/*
* bp can be NULL due to lazy debug register switching
* or due to concurrent perf counter removing.
*/
if (!bp) {
rcu_read_unlock();
break;
}
perf_bp_event(bp, args->regs); perf_bp_event(bp, args->regs);
...@@ -538,11 +536,10 @@ static int hw_breakpoint_handler(struct die_args *args) ...@@ -538,11 +536,10 @@ static int hw_breakpoint_handler(struct die_args *args)
* Set up resume flag to avoid breakpoint recursion when * Set up resume flag to avoid breakpoint recursion when
* returning back to origin. * returning back to origin.
*/ */
if (bp->hw.info.type == X86_BREAKPOINT_EXECUTE) if (bpx)
args->regs->flags |= X86_EFLAGS_RF; args->regs->flags |= X86_EFLAGS_RF;
rcu_read_unlock();
} }
/* /*
* Further processing in do_debug() is needed for a) user-space * Further processing in do_debug() is needed for a) user-space
* breakpoints (to generate signals) and b) when the system has * breakpoints (to generate signals) and b) when the system has
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment