Skip to content

Commit 24ebec2

Browse files
committed
arm64: hw_breakpoint: Don't invoke overflow handler on uaccess watchpoints
Unprivileged memory accesses generated by the so-called "translated" instructions (e.g. STTR) at EL1 can cause EL0 watchpoints to fire unexpectedly if kernel debugging is enabled. In such cases, the hw_breakpoint logic will invoke the user overflow handler which will typically raise a SIGTRAP back to the current task. This is futile when returning back to the kernel because (a) the signal won't have been delivered and (b) userspace can't handle the thing anyway. Avoid invoking the user overflow handler for watchpoints triggered by kernel uaccess routines, and instead single-step over the faulting instruction as we would if no overflow handler had been installed. (Fixes tag identifies the introduction of unprivileged memory accesses, which exposed this latent bug in the hw_breakpoint code) Cc: Catalin Marinas <[email protected]> Cc: James Morse <[email protected]> Fixes: 57f4959 ("arm64: kernel: Add support for User Access Override") Reported-by: Luis Machado <[email protected]> Signed-off-by: Will Deacon <[email protected]>
1 parent bf508ec commit 24ebec2

File tree

1 file changed

+26
-18
lines changed

1 file changed

+26
-18
lines changed

arch/arm64/kernel/hw_breakpoint.c

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,27 @@ static u64 get_distance_from_watchpoint(unsigned long addr, u64 val,
730730
return 0;
731731
}
732732

733+
static int watchpoint_report(struct perf_event *wp, unsigned long addr,
734+
struct pt_regs *regs)
735+
{
736+
int step = is_default_overflow_handler(wp);
737+
struct arch_hw_breakpoint *info = counter_arch_bp(wp);
738+
739+
info->trigger = addr;
740+
741+
/*
742+
* If we triggered a user watchpoint from a uaccess routine, then
743+
* handle the stepping ourselves since userspace really can't help
744+
* us with this.
745+
*/
746+
if (!user_mode(regs) && info->ctrl.privilege == AARCH64_BREAKPOINT_EL0)
747+
step = 1;
748+
else
749+
perf_bp_event(wp, regs);
750+
751+
return step;
752+
}
753+
733754
static int watchpoint_handler(unsigned long addr, unsigned int esr,
734755
struct pt_regs *regs)
735756
{
@@ -739,7 +760,6 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
739760
u64 val;
740761
struct perf_event *wp, **slots;
741762
struct debug_info *debug_info;
742-
struct arch_hw_breakpoint *info;
743763
struct arch_hw_breakpoint_ctrl ctrl;
744764

745765
slots = this_cpu_ptr(wp_on_reg);
@@ -777,25 +797,13 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,
777797
if (dist != 0)
778798
continue;
779799

780-
info = counter_arch_bp(wp);
781-
info->trigger = addr;
782-
perf_bp_event(wp, regs);
783-
784-
/* Do we need to handle the stepping? */
785-
if (is_default_overflow_handler(wp))
786-
step = 1;
800+
step = watchpoint_report(wp, addr, regs);
787801
}
788-
if (min_dist > 0 && min_dist != -1) {
789-
/* No exact match found. */
790-
wp = slots[closest_match];
791-
info = counter_arch_bp(wp);
792-
info->trigger = addr;
793-
perf_bp_event(wp, regs);
794802

795-
/* Do we need to handle the stepping? */
796-
if (is_default_overflow_handler(wp))
797-
step = 1;
798-
}
803+
/* No exact match found? */
804+
if (min_dist > 0 && min_dist != -1)
805+
step = watchpoint_report(slots[closest_match], addr, regs);
806+
799807
rcu_read_unlock();
800808

801809
if (!step)

0 commit comments

Comments
 (0)