| From eec13b42d41b0f3339dcf0c4da43734427c68620 Mon Sep 17 00:00:00 2001 |
| From: Will Deacon <will@kernel.org> |
| Date: Thu, 18 Jun 2020 11:16:45 +0100 |
| Subject: ARM: 8986/1: hw_breakpoint: Don't invoke overflow handler on uaccess watchpoints |
| |
| From: Will Deacon <will@kernel.org> |
| |
| commit eec13b42d41b0f3339dcf0c4da43734427c68620 upstream. |
| |
| Unprivileged memory accesses generated by the so-called "translated" |
| instructions (e.g. LDRT) in kernel mode can cause user watchpoints to fire |
| unexpectedly. 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. |
| |
| Cc: <stable@vger.kernel.org> |
| Fixes: f81ef4a920c8 ("ARM: 6356/1: hw-breakpoint: add ARM backend for the hw-breakpoint framework") |
| Reported-by: Luis Machado <luis.machado@linaro.org> |
| Tested-by: Luis Machado <luis.machado@linaro.org> |
| Signed-off-by: Will Deacon <will@kernel.org> |
| Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/arm/kernel/hw_breakpoint.c | 27 ++++++++++++++++++++++----- |
| 1 file changed, 22 insertions(+), 5 deletions(-) |
| |
| --- a/arch/arm/kernel/hw_breakpoint.c |
| +++ b/arch/arm/kernel/hw_breakpoint.c |
| @@ -688,6 +688,12 @@ static void disable_single_step(struct p |
| arch_install_hw_breakpoint(bp); |
| } |
| |
| +static int watchpoint_fault_on_uaccess(struct pt_regs *regs, |
| + struct arch_hw_breakpoint *info) |
| +{ |
| + return !user_mode(regs) && info->ctrl.privilege == ARM_BREAKPOINT_USER; |
| +} |
| + |
| static void watchpoint_handler(unsigned long addr, unsigned int fsr, |
| struct pt_regs *regs) |
| { |
| @@ -747,16 +753,27 @@ static void watchpoint_handler(unsigned |
| } |
| |
| pr_debug("watchpoint fired: address = 0x%x\n", info->trigger); |
| + |
| + /* |
| + * If we triggered a user watchpoint from a uaccess routine, |
| + * then handle the stepping ourselves since userspace really |
| + * can't help us with this. |
| + */ |
| + if (watchpoint_fault_on_uaccess(regs, info)) |
| + goto step; |
| + |
| perf_bp_event(wp, regs); |
| |
| /* |
| - * If no overflow handler is present, insert a temporary |
| - * mismatch breakpoint so we can single-step over the |
| - * watchpoint trigger. |
| + * Defer stepping to the overflow handler if one is installed. |
| + * Otherwise, insert a temporary mismatch breakpoint so that |
| + * we can single-step over the watchpoint trigger. |
| */ |
| - if (is_default_overflow_handler(wp)) |
| - enable_single_step(wp, instruction_pointer(regs)); |
| + if (!is_default_overflow_handler(wp)) |
| + goto unlock; |
| |
| +step: |
| + enable_single_step(wp, instruction_pointer(regs)); |
| unlock: |
| rcu_read_unlock(); |
| } |