Re: [RFC PATCH 3/9] arm64/debug-monitors: Make do_el1_brk64()/do_el1_softstep() noinstr

Hongyan Xia <[email protected]>
Newsgroups org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On 7/31/2026 11:25 PM, Mark Rutland wrote:
> [Some people who received this message don't often get email from [email protected]. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
> 
> On Mon, Jul 27, 2026 at 12:25:38PM +0000, Hongyan Xia wrote:
>> From: Hongyan Xia <[email protected]>
>>
>> Convert do_el1_brk64(), do_el1_softstep() and call_el1_break_hook() to
>> noinstr. The kprobe and kretprobe BRK handlers (converted to noinstr in
>> the following patches) are dispatched directly. Every other BRK handler
>> are ordinary instrumentable code and now run bounded by
>> instrumentation_begin()/end().
> 
> Why is it necessary to change do_el1_softstep()?
> 
> Neither kprobes nor kretprobes uses software stepping since commit:
> 
>    7ee31a3aa8f4 ("arm64: kprobes: Use BRK instead of single-step when executing instructions out-of-line")
> 
> ... so either that shouldn't be necessary, or there's a problem that
> needs to be described in this commit message.
> 
>> With this, everything on the el1 debug exception path from the vectors
>> down to the kprobe handlers is noinstr, and instrumentation only runs
>> inside explicit instrumentation windows.
>>
>> Signed-off-by: Hongyan Xia <[email protected]>
>> ---
>>   arch/arm64/kernel/debug-monitors.c | 74 +++++++++++++++++-------------
>>   1 file changed, 41 insertions(+), 33 deletions(-)
>>
>> diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
>> index 29307642f4c9..a970ab6327cd 100644
>> --- a/arch/arm64/kernel/debug-monitors.c
>> +++ b/arch/arm64/kernel/debug-monitors.c
>> @@ -11,6 +11,7 @@
>>   #include <linux/debugfs.h>
>>   #include <linux/hardirq.h>
>>   #include <linux/init.h>
>> +#include <linux/instrumentation.h>
>>   #include <linux/ptrace.h>
>>   #include <linux/kprobes.h>
>>   #include <linux/stat.h>
>> @@ -193,59 +194,65 @@ void do_el0_softstep(unsigned long esr, struct pt_regs *regs)
>>        user_rewind_single_step(current);
>>   }
>>
>> -void do_el1_softstep(unsigned long esr, struct pt_regs *regs)
>> +void noinstr do_el1_softstep(unsigned long esr, struct pt_regs *regs)
>>   {
>> -     if (kgdb_single_step_handler(regs, esr) == DBG_HOOK_HANDLED)
>> +     int handled;
>> +
>> +     instrumentation_begin();
>> +     handled = kgdb_single_step_handler(regs, esr);
>> +     instrumentation_end();
>> +
>> +     if (handled == DBG_HOOK_HANDLED)
>>                return;
>>
>> +     instrumentation_begin();
>>        pr_warn("Unexpected kernel single-step exception at EL1\n");
>> +     instrumentation_end();
>>        /*
>>         * Re-enable stepping since we know that we will be
>>         * returning to regs.
>>         */
>>        set_regs_spsr_ss(regs);
>>   }
>> -NOKPROBE_SYMBOL(do_el1_softstep);
> 
> As above, I don't think it's necessary to change do_el1_softstep(), but
> I might be missing something that you haven't described in the commit
> message.

We have a noinstr verifier locally. To make it happy I started out this 
series trying to make the whole debug_exception() noinstr, but I see it 
has caused enough confusion (like in 1/9) and I'll limit the scope to 
only Kprobe noinstr in the next revision.

> Is the existing NOKPROBE_SYMBOL() annotation actually necessary? It
> looks like that dates from before commit 7ee31a3aa8f4, and I suspect we
> can delete it even without making this noinstr.
> 
> I don't think you need to make structural changes here.  Given the first
> thing the function does is an unconditional call to an instrumented
> function, we're not gaining anything by litering this with
> instrumentation_{begin,end}().
> 
>> -static int call_el1_break_hook(struct pt_regs *regs, unsigned long esr)
>> +static int noinstr call_el1_break_hook(struct pt_regs *regs, unsigned long esr)
>>   {
>> -     if (esr_brk_comment(esr) == BUG_BRK_IMM)
>> -             return bug_brk_handler(regs, esr);
>> -
>> -     if (IS_ENABLED(CONFIG_CFI) && esr_is_cfi_brk(esr))
>> -             return cfi_brk_handler(regs, esr);
>> -
>> -     if (esr_brk_comment(esr) == FAULT_BRK_IMM)
>> -             return reserved_fault_brk_handler(regs, esr);
>> -
>> -     if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) &&
>> -             (esr_brk_comment(esr) & ~KASAN_BRK_MASK) == KASAN_BRK_IMM)
>> -             return kasan_brk_handler(regs, esr);
>> -
>> -     if (IS_ENABLED(CONFIG_UBSAN_TRAP) && esr_is_ubsan_brk(esr))
>> -             return ubsan_brk_handler(regs, esr);
>> -
>> -     if (IS_ENABLED(CONFIG_KGDB)) {
>> -             if (esr_brk_comment(esr) == KGDB_DYN_DBG_BRK_IMM)
>> -                     return kgdb_brk_handler(regs, esr);
>> -             if (esr_brk_comment(esr) == KGDB_COMPILED_DBG_BRK_IMM)
>> -                     return kgdb_compiled_brk_handler(regs, esr);
>> -     }
>> +     unsigned long comment = esr_brk_comment(esr);
>> +     int ret = DBG_HOOK_ERROR;
>>
>>        if (IS_ENABLED(CONFIG_KPROBES)) {
>> -             if (esr_brk_comment(esr) == KPROBES_BRK_IMM)
>> +             if (comment == KPROBES_BRK_IMM)
>>                        return kprobe_brk_handler(regs, esr);
>> -             if (esr_brk_comment(esr) == KPROBES_BRK_SS_IMM)
>> +             if (comment == KPROBES_BRK_SS_IMM)
>>                        return kprobe_ss_brk_handler(regs, esr);
>>        }
>>
>>        if (IS_ENABLED(CONFIG_KRETPROBES) &&
>> -             esr_brk_comment(esr) == KRETPROBES_BRK_IMM)
>> +         comment == KRETPROBES_BRK_IMM)
>>                return kretprobe_brk_handler(regs, esr);
>>
>> -     return DBG_HOOK_ERROR;
>> +     instrumentation_begin();
>> +     if (comment == BUG_BRK_IMM)
>> +             ret = bug_brk_handler(regs, esr);
>> +     else if (IS_ENABLED(CONFIG_CFI) && esr_is_cfi_brk(esr))
>> +             ret = cfi_brk_handler(regs, esr);
>> +     else if (comment == FAULT_BRK_IMM)
>> +             ret = reserved_fault_brk_handler(regs, esr);
>> +     else if (IS_ENABLED(CONFIG_KASAN_SW_TAGS) &&
>> +              (comment & ~KASAN_BRK_MASK) == KASAN_BRK_IMM)
>> +             ret = kasan_brk_handler(regs, esr);
>> +     else if (IS_ENABLED(CONFIG_UBSAN_TRAP) && esr_is_ubsan_brk(esr))
>> +             ret = ubsan_brk_handler(regs, esr);
>> +     else if (IS_ENABLED(CONFIG_KGDB)) {
>> +             if (comment == KGDB_DYN_DBG_BRK_IMM)
>> +                     ret = kgdb_brk_handler(regs, esr);
>> +             else if (comment == KGDB_COMPILED_DBG_BRK_IMM)
>> +                     ret = kgdb_compiled_brk_handler(regs, esr);
>> +     }
>> +     instrumentation_end();
>> +
>> +     return ret;
>>   }
>> -NOKPROBE_SYMBOL(call_el1_break_hook);
> 
> I don't think you need to make any structural changes to
> call_el1_break_hook(). Just mark it as noinstr, and remove the
> NOKPROBE_SYMBOL() annotation. The existing control flow will be safe.
> 
>>   /*
>>    * We have already unmasked interrupts and enabled preemption
>> @@ -261,14 +268,15 @@ void do_el0_brk64(unsigned long esr, struct pt_regs *regs)
>>        send_user_sigtrap(TRAP_BRKPT);
>>   }
>>
>> -void do_el1_brk64(unsigned long esr, struct pt_regs *regs)
>> +void noinstr do_el1_brk64(unsigned long esr, struct pt_regs *regs)
>>   {
>>        if (call_el1_break_hook(regs, esr) == DBG_HOOK_HANDLED)
>>                return;
>>
>> +     instrumentation_begin();
>>        die("Oops - BRK", regs, esr);
>> +     instrumentation_end();
>>   }
>> -NOKPROBE_SYMBOL(do_el1_brk64);
> 
> Likewise, just mark do_el1_brk64() as noinstr and remove the
> NOKPROBE_SYMBOL() annotation, without the instrumentation_{begin,end}()
> calls.

I'll just drop this patch and get Kprobe properly done first.

> 
> Mark.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.