Re: [REGRESSION] 32-bit ARM's BKPT instruction no longer works
slipher <[email protected]> Sun, 21 Jun 2026 21:53:17 +0000
| Newsgroups | dev.linux.lists.regressions,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <zbEOoL4_phCCh7td7DwqQtyhleFJ_G3yRBc8AFjT5hwIwcRipRMsjtIVJgLi3t5vJk5QsuKjY9c8I91nuVSYk4-xlzrAqigMWIE0iNMc9PM=@protonmail.com> |
On Sunday, June 21st, 2026 at 3:19 PM, Russell King (Oracle) <[email protected]> wrote: > On Sun, Jun 21, 2026 at 07:15:27PM +0000, slipher wrote: > > Consider the C program for 32-bit ARM architectures: > > > > int main() { > > __asm__ __volatile__ ("BKPT"); > > return 0; > > } > > > > > > Expected behavior is that this raises SIGTRAP. Since Linux 6.10 this no > > longer happens; instead execution perpetually resumes at the same > > instruction, using 100% of CPU. It does not matter whether GDB is > > attached. I have tested with an armv7l CPU, but I imagine any other > > variants with the BKPT instruction would be equally affected. > > Looking at the code, I doubt this has ever cleanly raised SIGTRAP (can > you check whether it does in kernels without c3f89986fde please?) > > What I suspect instead is you get an "Unhandled ... abort" instead > and the program forcefully killed as hw_breakpoint_pending() would > have ARM_DSCR_MOE(dscr) == 3, and the switch() would set ret = 1. > That triggers the fault handlers in arch/arm/mm/fault.c to > complain bitterly, and forced a SIGTRAP to the program to kill it > off. No resumption from an unhandled trap is expected. I have tested with a 6.6 kernel. All of that is correct, as detailed in the aforementioned blog post, except the last sentence. The switch does set ret = 1, thereby passing on the exception. The kernel complains, with such lines in dmesg output: [ 1547.164526] Unhandled prefetch abort: breakpoint debug exception (0x222) at 0x0001051c Indeed, it is not clean or efficient; the blog (https://www.jwhitham.org/2015/04/the-mystery-of-fifteen-millisecond.html) even has a proposed patch to improve the performance when raising SIGTRAP. However, it is possible to catch the signal, and even resume with something like this: #include <ucontext.h> #include <signal.h> #include <stdio.h> void handl(int a, siginfo_t *b, void *uc) { puts("caught SIGTRAP"); ((ucontext_t*)uc)->uc_mcontext.arm_pc += 4; } int main() { struct sigaction s; s.sa_flags = SA_SIGINFO; s.sa_sigaction = handl; sigemptyset(&s.sa_mask); sigaction(SIGTRAP, &s, 0); puts("start"); __asm__ __volatile__("BKPT"); puts("resumed"); return 0; } Re-testing, I realized there is a huge caveat: SIGTRAP is *not* raised when running under a debugger! If GDB is attached, either of the C programs above will repeatedly resume at the faulting instruction on Linux 6.6, just as they will with the latest kernels. So the regression only affects the perhaps-obscure case of using BKPT without any intention of attaching a debugger, unless that worked in even-earlier versions of Linux.