Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: 9316/1: hw_breakpoint: fix single-stepping when using bpf_overflow_handler

Arm platforms use is_default_overflow_handler() to determine if the
hw_breakpoint code should single-step over the breakpoint trigger or
let the custom handler deal with it.

Since bpf_overflow_handler() currently isn't recognized as a default
handler, attaching a BPF program to a PERF_TYPE_BREAKPOINT event causes
it to keep firing (the instruction triggering the data abort exception
is never skipped). For example:

# bpftrace -e 'watchpoint:0x10000:4:w { print("hit") }' -c ./test
Attaching 1 probe...
hit
hit
[...]
^C

(./test performs a single 4-byte store to 0x10000)

This patch replaces the check with uses_default_overflow_handler(),
which accounts for the bpf_overflow_handler() case by also testing
if one of the perf_event_output functions gets invoked indirectly,
via orig_default_handler.

Link: https://lore.kernel.org/linux-arm-kernel/20220923203644.2731604-1-tnovak@fb.com/

Signed-off-by: Tomislav Novak <tnovak@fb.com>
Tested-by: Samuel Gosselin <sgosselin@google.com> # arm64
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>

authored by

Tomislav Novak and committed by
Russell King (Oracle)
e6b51532 06c2afb8

+25 -9
+4 -4
arch/arm/kernel/hw_breakpoint.c
··· 626 626 hw->address &= ~alignment_mask; 627 627 hw->ctrl.len <<= offset; 628 628 629 - if (is_default_overflow_handler(bp)) { 629 + if (uses_default_overflow_handler(bp)) { 630 630 /* 631 631 * Mismatch breakpoints are required for single-stepping 632 632 * breakpoints. ··· 798 798 * Otherwise, insert a temporary mismatch breakpoint so that 799 799 * we can single-step over the watchpoint trigger. 800 800 */ 801 - if (!is_default_overflow_handler(wp)) 801 + if (!uses_default_overflow_handler(wp)) 802 802 continue; 803 803 step: 804 804 enable_single_step(wp, instruction_pointer(regs)); ··· 811 811 info->trigger = addr; 812 812 pr_debug("watchpoint fired: address = 0x%x\n", info->trigger); 813 813 perf_bp_event(wp, regs); 814 - if (is_default_overflow_handler(wp)) 814 + if (uses_default_overflow_handler(wp)) 815 815 enable_single_step(wp, instruction_pointer(regs)); 816 816 } 817 817 ··· 886 886 info->trigger = addr; 887 887 pr_debug("breakpoint fired: address = 0x%x\n", addr); 888 888 perf_bp_event(bp, regs); 889 - if (is_default_overflow_handler(bp)) 889 + if (uses_default_overflow_handler(bp)) 890 890 enable_single_step(bp, addr); 891 891 goto unlock; 892 892 }
+2 -2
arch/arm64/kernel/hw_breakpoint.c
··· 654 654 perf_bp_event(bp, regs); 655 655 656 656 /* Do we need to handle the stepping? */ 657 - if (is_default_overflow_handler(bp)) 657 + if (uses_default_overflow_handler(bp)) 658 658 step = 1; 659 659 unlock: 660 660 rcu_read_unlock(); ··· 733 733 static int watchpoint_report(struct perf_event *wp, unsigned long addr, 734 734 struct pt_regs *regs) 735 735 { 736 - int step = is_default_overflow_handler(wp); 736 + int step = uses_default_overflow_handler(wp); 737 737 struct arch_hw_breakpoint *info = counter_arch_bp(wp); 738 738 739 739 info->trigger = addr;
+19 -3
include/linux/perf_event.h
··· 1316 1316 struct pt_regs *regs); 1317 1317 1318 1318 static inline bool 1319 - is_default_overflow_handler(struct perf_event *event) 1319 + __is_default_overflow_handler(perf_overflow_handler_t overflow_handler) 1320 1320 { 1321 - if (likely(event->overflow_handler == perf_event_output_forward)) 1321 + if (likely(overflow_handler == perf_event_output_forward)) 1322 1322 return true; 1323 - if (unlikely(event->overflow_handler == perf_event_output_backward)) 1323 + if (unlikely(overflow_handler == perf_event_output_backward)) 1324 1324 return true; 1325 1325 return false; 1326 1326 } 1327 + 1328 + #define is_default_overflow_handler(event) \ 1329 + __is_default_overflow_handler((event)->overflow_handler) 1330 + 1331 + #ifdef CONFIG_BPF_SYSCALL 1332 + static inline bool uses_default_overflow_handler(struct perf_event *event) 1333 + { 1334 + if (likely(is_default_overflow_handler(event))) 1335 + return true; 1336 + 1337 + return __is_default_overflow_handler(event->orig_overflow_handler); 1338 + } 1339 + #else 1340 + #define uses_default_overflow_handler(event) \ 1341 + is_default_overflow_handler(event) 1342 + #endif 1327 1343 1328 1344 extern void 1329 1345 perf_event_header__init_id(struct perf_event_header *header,