* [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults
2026-07-06 8:36 ` [RFC 0/3] " Pu Hu
@ 2026-07-06 8:36 ` Pu Hu
2026-07-08 0:46 ` Masami Hiramatsu
2026-07-06 8:36 ` [RFC 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping Pu Hu
` (2 subsequent siblings)
3 siblings, 1 reply; 23+ messages in thread
From: Pu Hu @ 2026-07-06 8:36 UTC (permalink / raw)
To: mhiramat
Cc: ada.coupriediaz, catalin.marinas, davem, Hongyan Xia, Pu Hu,
Jiazi Li, linux-arm-kernel, linux-kernel, linux-trace-kernel,
naveen, will, yang
From: Pu Hu <hupu@transsion.com>
kprobe_fault_handler() handles faults taken while kprobes is in
KPROBE_HIT_SS or KPROBE_REENTER state as faults caused by the
single-stepped instruction.
That assumption is not always true. While a kprobe is preparing or
executing the out-of-line single-step instruction, other code may run
in that window. For example, perf or trace code can be invoked from the
debug exception path and may take a fault of its own. In that case the
fault did not happen on the kprobe XOL instruction, but the kprobe fault
handler may still try to recover it as a kprobe single-step fault.
This can corrupt the exception recovery flow and leave the real fault to
be handled with a wrong PC. A typical reproducer is running simpleperf
with preemptirq tracepoints and dwarf callchains while a kprobe is
installed on a frequently executed kernel function.
Fix this by handling faults in KPROBE_HIT_SS/KPROBE_REENTER only when
the faulting PC points at the current kprobe's XOL instruction. Faults
from any other PC are left to the normal fault handling path.
This follows the same idea as the x86 fix in commit 6381c24cd6d5
("kprobes/x86: Fix page-fault handling logic").
Signed-off-by: Pu Hu <hupu@transsion.com>
Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
---
arch/arm64/kernel/probes/kprobes.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index 43a0361a8bf0..e4d2852ce2fb 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -285,6 +285,20 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
switch (kcb->kprobe_status) {
case KPROBE_HIT_SS:
case KPROBE_REENTER:
+ /*
+ * A fault taken while a kprobe is single-stepping is not
+ * necessarily caused by the instruction in the XOL slot. For
+ * example, tracing or perf code running in this window may take
+ * an unrelated fault.
+ *
+ * Handle the fault here only when the faulting PC is the XOL
+ * instruction of the current kprobe. Otherwise let the normal
+ * fault handling path deal with it.
+ */
+ if (cur->ainsn.xol_insn &&
+ instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
+ break;
+
/*
* We are here because the instruction being single
* stepped caused a page fault. We reset the current
--
2.43.0
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults
2026-07-06 8:36 ` [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults Pu Hu
@ 2026-07-08 0:46 ` Masami Hiramatsu
2026-07-08 5:57 ` Hongyan Xia
0 siblings, 1 reply; 23+ messages in thread
From: Masami Hiramatsu @ 2026-07-08 0:46 UTC (permalink / raw)
To: Pu Hu
Cc: ada.coupriediaz, catalin.marinas, davem, Hongyan Xia, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
On Mon, 6 Jul 2026 08:36:48 +0000
Pu Hu <hupu@transsion.com> wrote:
> From: Pu Hu <hupu@transsion.com>
>
> kprobe_fault_handler() handles faults taken while kprobes is in
> KPROBE_HIT_SS or KPROBE_REENTER state as faults caused by the
> single-stepped instruction.
>
> That assumption is not always true. While a kprobe is preparing or
> executing the out-of-line single-step instruction, other code may run
> in that window. For example, perf or trace code can be invoked from the
> debug exception path and may take a fault of its own. In that case the
> fault did not happen on the kprobe XOL instruction, but the kprobe fault
> handler may still try to recover it as a kprobe single-step fault.
>
> This can corrupt the exception recovery flow and leave the real fault to
> be handled with a wrong PC. A typical reproducer is running simpleperf
> with preemptirq tracepoints and dwarf callchains while a kprobe is
> installed on a frequently executed kernel function.
>
> Fix this by handling faults in KPROBE_HIT_SS/KPROBE_REENTER only when
> the faulting PC points at the current kprobe's XOL instruction. Faults
> from any other PC are left to the normal fault handling path.
>
> This follows the same idea as the x86 fix in commit 6381c24cd6d5
> ("kprobes/x86: Fix page-fault handling logic").
>
> Signed-off-by: Pu Hu <hupu@transsion.com>
> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
> ---
> arch/arm64/kernel/probes/kprobes.c | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> index 43a0361a8bf0..e4d2852ce2fb 100644
> --- a/arch/arm64/kernel/probes/kprobes.c
> +++ b/arch/arm64/kernel/probes/kprobes.c
> @@ -285,6 +285,20 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
> switch (kcb->kprobe_status) {
> case KPROBE_HIT_SS:
> case KPROBE_REENTER:
> + /*
> + * A fault taken while a kprobe is single-stepping is not
> + * necessarily caused by the instruction in the XOL slot. For
> + * example, tracing or perf code running in this window may take
> + * an unrelated fault.
> + *
> + * Handle the fault here only when the faulting PC is the XOL
> + * instruction of the current kprobe. Otherwise let the normal
> + * fault handling path deal with it.
> + */
> + if (cur->ainsn.xol_insn &&
> + instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
> + break;
Can you check Sashiko's comments[1]?
[1] https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com?part=1
It seems that it complains about simulated kprobe's case.
In that case, cur->ainsn.xol_insn == NULL. The simulation should be done
in the kprobe context (which is a debug trap). I'm not sure the arm64
can cause NMI in that context, but if it happens and causes a fault,
it may cause a problem.
So I think we can just ignore the fault on the simulated kprobes.
To ensure that, you can just add:
if (cur && !cur->ainsn.xol_insn)
return 0;
at the entry of this function. (and remove redundant cur->ainsn.xol_insn check)
Thank you,
> +
> /*
> * We are here because the instruction being single
> * stepped caused a page fault. We reset the current
> --
> 2.43.0
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults
2026-07-08 0:46 ` Masami Hiramatsu
@ 2026-07-08 5:57 ` Hongyan Xia
2026-07-08 7:42 ` Masami Hiramatsu
0 siblings, 1 reply; 23+ messages in thread
From: Hongyan Xia @ 2026-07-08 5:57 UTC (permalink / raw)
To: Masami Hiramatsu (Google), Pu Hu
Cc: ada.coupriediaz, catalin.marinas, davem, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
Hi Masami,
On 7/8/2026 8:46 AM, Masami Hiramatsu wrote:
> On Mon, 6 Jul 2026 08:36:48 +0000
> Pu Hu <hupu@transsion.com> wrote:
>
>> From: Pu Hu <hupu@transsion.com>
>>
>> kprobe_fault_handler() handles faults taken while kprobes is in
>> KPROBE_HIT_SS or KPROBE_REENTER state as faults caused by the
>> single-stepped instruction.
>>
>> That assumption is not always true. While a kprobe is preparing or
>> executing the out-of-line single-step instruction, other code may run
>> in that window. For example, perf or trace code can be invoked from the
>> debug exception path and may take a fault of its own. In that case the
>> fault did not happen on the kprobe XOL instruction, but the kprobe fault
>> handler may still try to recover it as a kprobe single-step fault.
>>
>> This can corrupt the exception recovery flow and leave the real fault to
>> be handled with a wrong PC. A typical reproducer is running simpleperf
>> with preemptirq tracepoints and dwarf callchains while a kprobe is
>> installed on a frequently executed kernel function.
>>
>> Fix this by handling faults in KPROBE_HIT_SS/KPROBE_REENTER only when
>> the faulting PC points at the current kprobe's XOL instruction. Faults
>> from any other PC are left to the normal fault handling path.
>>
>> This follows the same idea as the x86 fix in commit 6381c24cd6d5
>> ("kprobes/x86: Fix page-fault handling logic").
>>
>> Signed-off-by: Pu Hu <hupu@transsion.com>
>> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
>> ---
>> arch/arm64/kernel/probes/kprobes.c | 14 ++++++++++++++
>> 1 file changed, 14 insertions(+)
>>
>> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
>> index 43a0361a8bf0..e4d2852ce2fb 100644
>> --- a/arch/arm64/kernel/probes/kprobes.c
>> +++ b/arch/arm64/kernel/probes/kprobes.c
>> @@ -285,6 +285,20 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
>> switch (kcb->kprobe_status) {
>> case KPROBE_HIT_SS:
>> case KPROBE_REENTER:
>> + /*
>> + * A fault taken while a kprobe is single-stepping is not
>> + * necessarily caused by the instruction in the XOL slot. For
>> + * example, tracing or perf code running in this window may take
>> + * an unrelated fault.
>> + *
>> + * Handle the fault here only when the faulting PC is the XOL
>> + * instruction of the current kprobe. Otherwise let the normal
>> + * fault handling path deal with it.
>> + */
>> + if (cur->ainsn.xol_insn &&
>> + instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
>> + break;
>
> Can you check Sashiko's comments[1]?
>
> [1] https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com?part=1
>
> It seems that it complains about simulated kprobe's case.
> In that case, cur->ainsn.xol_insn == NULL. The simulation should be done
> in the kprobe context (which is a debug trap). I'm not sure the arm64
> can cause NMI in that context, but if it happens and causes a fault,
> it may cause a problem.
>
> So I think we can just ignore the fault on the simulated kprobes.
>
> To ensure that, you can just add:
>
> if (cur && !cur->ainsn.xol_insn)
> return 0;
>
> at the entry of this function. (and remove redundant cur->ainsn.xol_insn check)
Right, both cases:
1. single-step XOL
2. simulated
have this problem and this patch fixed 1. 2 remains unchanged.
Ideally we should fix both, but the simulated case seems more
complicated, and at least we didn't make things worse for 2. So I wonder
if we can analyze 2 more thoroughly and fix it in a separate patch.
> Thank you,
>
>> +
>> /*
>> * We are here because the instruction being single
>> * stepped caused a page fault. We reset the current
>> --
>> 2.43.0
>>
> --
> Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults
2026-07-08 5:57 ` Hongyan Xia
@ 2026-07-08 7:42 ` Masami Hiramatsu
2026-07-08 8:55 ` Hongyan Xia
0 siblings, 1 reply; 23+ messages in thread
From: Masami Hiramatsu @ 2026-07-08 7:42 UTC (permalink / raw)
To: Hongyan Xia
Cc: Pu Hu, ada.coupriediaz, catalin.marinas, davem, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
On Wed, 8 Jul 2026 05:57:24 +0000
Hongyan Xia <hongyan.xia@transsion.com> wrote:
> Hi Masami,
>
> On 7/8/2026 8:46 AM, Masami Hiramatsu wrote:
> > On Mon, 6 Jul 2026 08:36:48 +0000
> > Pu Hu <hupu@transsion.com> wrote:
> >
> >> From: Pu Hu <hupu@transsion.com>
> >>
> >> kprobe_fault_handler() handles faults taken while kprobes is in
> >> KPROBE_HIT_SS or KPROBE_REENTER state as faults caused by the
> >> single-stepped instruction.
> >>
> >> That assumption is not always true. While a kprobe is preparing or
> >> executing the out-of-line single-step instruction, other code may run
> >> in that window. For example, perf or trace code can be invoked from the
> >> debug exception path and may take a fault of its own. In that case the
> >> fault did not happen on the kprobe XOL instruction, but the kprobe fault
> >> handler may still try to recover it as a kprobe single-step fault.
> >>
> >> This can corrupt the exception recovery flow and leave the real fault to
> >> be handled with a wrong PC. A typical reproducer is running simpleperf
> >> with preemptirq tracepoints and dwarf callchains while a kprobe is
> >> installed on a frequently executed kernel function.
> >>
> >> Fix this by handling faults in KPROBE_HIT_SS/KPROBE_REENTER only when
> >> the faulting PC points at the current kprobe's XOL instruction. Faults
> >> from any other PC are left to the normal fault handling path.
> >>
> >> This follows the same idea as the x86 fix in commit 6381c24cd6d5
> >> ("kprobes/x86: Fix page-fault handling logic").
> >>
> >> Signed-off-by: Pu Hu <hupu@transsion.com>
> >> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
> >> ---
> >> arch/arm64/kernel/probes/kprobes.c | 14 ++++++++++++++
> >> 1 file changed, 14 insertions(+)
> >>
> >> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> >> index 43a0361a8bf0..e4d2852ce2fb 100644
> >> --- a/arch/arm64/kernel/probes/kprobes.c
> >> +++ b/arch/arm64/kernel/probes/kprobes.c
> >> @@ -285,6 +285,20 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
> >> switch (kcb->kprobe_status) {
> >> case KPROBE_HIT_SS:
> >> case KPROBE_REENTER:
> >> + /*
> >> + * A fault taken while a kprobe is single-stepping is not
> >> + * necessarily caused by the instruction in the XOL slot. For
> >> + * example, tracing or perf code running in this window may take
> >> + * an unrelated fault.
> >> + *
> >> + * Handle the fault here only when the faulting PC is the XOL
> >> + * instruction of the current kprobe. Otherwise let the normal
> >> + * fault handling path deal with it.
> >> + */
> >> + if (cur->ainsn.xol_insn &&
> >> + instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
> >> + break;
> >
> > Can you check Sashiko's comments[1]?
> >
> > [1] https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com?part=1
> >
> > It seems that it complains about simulated kprobe's case.
> > In that case, cur->ainsn.xol_insn == NULL. The simulation should be done
> > in the kprobe context (which is a debug trap). I'm not sure the arm64
> > can cause NMI in that context, but if it happens and causes a fault,
> > it may cause a problem.
> >
> > So I think we can just ignore the fault on the simulated kprobes.
> >
> > To ensure that, you can just add:
> >
> > if (cur && !cur->ainsn.xol_insn)
> > return 0;
> >
> > at the entry of this function. (and remove redundant cur->ainsn.xol_insn check)
>
> Right, both cases:
>
> 1. single-step XOL
> 2. simulated
>
> have this problem and this patch fixed 1. 2 remains unchanged.
>
> Ideally we should fix both, but the simulated case seems more
> complicated, and at least we didn't make things worse for 2. So I wonder
> if we can analyze 2 more thoroughly and fix it in a separate patch.
OK, but basically this fault handler is only for the SS XOL,
not for simulated one (as same as x86). So just skip the
simulated case is enough in this patch.
(Note that x86 also have simulated path, and that is not handled
by the fault handler)
Thank you,
>
> > Thank you,
> >
> >> +
> >> /*
> >> * We are here because the instruction being single
> >> * stepped caused a page fault. We reset the current
> >> --
> >> 2.43.0
> >>
> > --
> > Masami Hiramatsu (Google) <mhiramat@kernel.org>
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults
2026-07-08 7:42 ` Masami Hiramatsu
@ 2026-07-08 8:55 ` Hongyan Xia
2026-07-08 11:59 ` Masami Hiramatsu
0 siblings, 1 reply; 23+ messages in thread
From: Hongyan Xia @ 2026-07-08 8:55 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: Pu Hu, ada.coupriediaz, catalin.marinas, davem, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
On 7/8/2026 3:42 PM, Masami Hiramatsu wrote:
> On Wed, 8 Jul 2026 05:57:24 +0000
> Hongyan Xia <hongyan.xia@transsion.com> wrote:
>
>> Hi Masami,
>>
>> On 7/8/2026 8:46 AM, Masami Hiramatsu wrote:
>>> On Mon, 6 Jul 2026 08:36:48 +0000
>>> Pu Hu <hupu@transsion.com> wrote:
>>>
>>>> From: Pu Hu <hupu@transsion.com>
>>>>
>>>> kprobe_fault_handler() handles faults taken while kprobes is in
>>>> KPROBE_HIT_SS or KPROBE_REENTER state as faults caused by the
>>>> single-stepped instruction.
>>>>
>>>> That assumption is not always true. While a kprobe is preparing or
>>>> executing the out-of-line single-step instruction, other code may run
>>>> in that window. For example, perf or trace code can be invoked from the
>>>> debug exception path and may take a fault of its own. In that case the
>>>> fault did not happen on the kprobe XOL instruction, but the kprobe fault
>>>> handler may still try to recover it as a kprobe single-step fault.
>>>>
>>>> This can corrupt the exception recovery flow and leave the real fault to
>>>> be handled with a wrong PC. A typical reproducer is running simpleperf
>>>> with preemptirq tracepoints and dwarf callchains while a kprobe is
>>>> installed on a frequently executed kernel function.
>>>>
>>>> Fix this by handling faults in KPROBE_HIT_SS/KPROBE_REENTER only when
>>>> the faulting PC points at the current kprobe's XOL instruction. Faults
>>>> from any other PC are left to the normal fault handling path.
>>>>
>>>> This follows the same idea as the x86 fix in commit 6381c24cd6d5
>>>> ("kprobes/x86: Fix page-fault handling logic").
>>>>
>>>> Signed-off-by: Pu Hu <hupu@transsion.com>
>>>> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
>>>> ---
>>>> arch/arm64/kernel/probes/kprobes.c | 14 ++++++++++++++
>>>> 1 file changed, 14 insertions(+)
>>>>
>>>> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
>>>> index 43a0361a8bf0..e4d2852ce2fb 100644
>>>> --- a/arch/arm64/kernel/probes/kprobes.c
>>>> +++ b/arch/arm64/kernel/probes/kprobes.c
>>>> @@ -285,6 +285,20 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
>>>> switch (kcb->kprobe_status) {
>>>> case KPROBE_HIT_SS:
>>>> case KPROBE_REENTER:
>>>> + /*
>>>> + * A fault taken while a kprobe is single-stepping is not
>>>> + * necessarily caused by the instruction in the XOL slot. For
>>>> + * example, tracing or perf code running in this window may take
>>>> + * an unrelated fault.
>>>> + *
>>>> + * Handle the fault here only when the faulting PC is the XOL
>>>> + * instruction of the current kprobe. Otherwise let the normal
>>>> + * fault handling path deal with it.
>>>> + */
>>>> + if (cur->ainsn.xol_insn &&
>>>> + instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
>>>> + break;
>>>
>>> Can you check Sashiko's comments[1]?
>>>
>>> [1] https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com?part=1
>>>
>>> It seems that it complains about simulated kprobe's case.
>>> In that case, cur->ainsn.xol_insn == NULL. The simulation should be done
>>> in the kprobe context (which is a debug trap). I'm not sure the arm64
>>> can cause NMI in that context, but if it happens and causes a fault,
>>> it may cause a problem.
>>>
>>> So I think we can just ignore the fault on the simulated kprobes.
>>>
>>> To ensure that, you can just add:
>>>
>>> if (cur && !cur->ainsn.xol_insn)
>>> return 0;
>>>
>>> at the entry of this function. (and remove redundant cur->ainsn.xol_insn check)
>>
>> Right, both cases:
>>
>> 1. single-step XOL
>> 2. simulated
>>
>> have this problem and this patch fixed 1. 2 remains unchanged.
>>
>> Ideally we should fix both, but the simulated case seems more
>> complicated, and at least we didn't make things worse for 2. So I wonder
>> if we can analyze 2 more thoroughly and fix it in a separate patch.
>
> OK, but basically this fault handler is only for the SS XOL,
> not for simulated one (as same as x86). So just skip the
> simulated case is enough in this patch.
> (Note that x86 also have simulated path, and that is not handled
> by the fault handler)
Hmm, what happens if the original PC is a simulated instruction that has
a recoverable ex_table entry that handles potential page fault, and this
PC also has an attached kprobe? Then the simulated case should be able
to enter kprobe_fault_handler()?
.ex_table:
insn foo, handler bar
foo:
LDR symbol # Load PC-relative. Simulated. Kprobe attached.
ret
When foo is called and the kprobe fires, it enters simulated path but
then LDR triggers a page fault. It then enters kprobe_fault_handler() to
fixup the PC. Then the fixup_exception() of the normal page fault finds
the ex_table entry and this case is successfully handled?
Looks like this handler can be entered by simulated instructions? Or
this is only theoretical and whoever arrange code like this should be
fired immediately?
>
> Thank you,
>
>>
>>> Thank you,
>>>
>>>> +
>>>> /*
>>>> * We are here because the instruction being single
>>>> * stepped caused a page fault. We reset the current
>>>> --
>>>> 2.43.0
>>>>
>>> --
>>> Masami Hiramatsu (Google) <mhiramat@kernel.org>
>>
>
>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults
2026-07-08 8:55 ` Hongyan Xia
@ 2026-07-08 11:59 ` Masami Hiramatsu
2026-07-08 12:12 ` Hongyan Xia
0 siblings, 1 reply; 23+ messages in thread
From: Masami Hiramatsu @ 2026-07-08 11:59 UTC (permalink / raw)
To: Hongyan Xia
Cc: Pu Hu, ada.coupriediaz, catalin.marinas, davem, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
On Wed, 8 Jul 2026 08:55:37 +0000
Hongyan Xia <hongyan.xia@transsion.com> wrote:
> On 7/8/2026 3:42 PM, Masami Hiramatsu wrote:
> > On Wed, 8 Jul 2026 05:57:24 +0000
> > Hongyan Xia <hongyan.xia@transsion.com> wrote:
> >
> >> Hi Masami,
> >>
> >> On 7/8/2026 8:46 AM, Masami Hiramatsu wrote:
> >>> On Mon, 6 Jul 2026 08:36:48 +0000
> >>> Pu Hu <hupu@transsion.com> wrote:
> >>>
> >>>> From: Pu Hu <hupu@transsion.com>
> >>>>
> >>>> kprobe_fault_handler() handles faults taken while kprobes is in
> >>>> KPROBE_HIT_SS or KPROBE_REENTER state as faults caused by the
> >>>> single-stepped instruction.
> >>>>
> >>>> That assumption is not always true. While a kprobe is preparing or
> >>>> executing the out-of-line single-step instruction, other code may run
> >>>> in that window. For example, perf or trace code can be invoked from the
> >>>> debug exception path and may take a fault of its own. In that case the
> >>>> fault did not happen on the kprobe XOL instruction, but the kprobe fault
> >>>> handler may still try to recover it as a kprobe single-step fault.
> >>>>
> >>>> This can corrupt the exception recovery flow and leave the real fault to
> >>>> be handled with a wrong PC. A typical reproducer is running simpleperf
> >>>> with preemptirq tracepoints and dwarf callchains while a kprobe is
> >>>> installed on a frequently executed kernel function.
> >>>>
> >>>> Fix this by handling faults in KPROBE_HIT_SS/KPROBE_REENTER only when
> >>>> the faulting PC points at the current kprobe's XOL instruction. Faults
> >>>> from any other PC are left to the normal fault handling path.
> >>>>
> >>>> This follows the same idea as the x86 fix in commit 6381c24cd6d5
> >>>> ("kprobes/x86: Fix page-fault handling logic").
> >>>>
> >>>> Signed-off-by: Pu Hu <hupu@transsion.com>
> >>>> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
> >>>> ---
> >>>> arch/arm64/kernel/probes/kprobes.c | 14 ++++++++++++++
> >>>> 1 file changed, 14 insertions(+)
> >>>>
> >>>> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> >>>> index 43a0361a8bf0..e4d2852ce2fb 100644
> >>>> --- a/arch/arm64/kernel/probes/kprobes.c
> >>>> +++ b/arch/arm64/kernel/probes/kprobes.c
> >>>> @@ -285,6 +285,20 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
> >>>> switch (kcb->kprobe_status) {
> >>>> case KPROBE_HIT_SS:
> >>>> case KPROBE_REENTER:
> >>>> + /*
> >>>> + * A fault taken while a kprobe is single-stepping is not
> >>>> + * necessarily caused by the instruction in the XOL slot. For
> >>>> + * example, tracing or perf code running in this window may take
> >>>> + * an unrelated fault.
> >>>> + *
> >>>> + * Handle the fault here only when the faulting PC is the XOL
> >>>> + * instruction of the current kprobe. Otherwise let the normal
> >>>> + * fault handling path deal with it.
> >>>> + */
> >>>> + if (cur->ainsn.xol_insn &&
> >>>> + instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
> >>>> + break;
> >>>
> >>> Can you check Sashiko's comments[1]?
> >>>
> >>> [1] https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com?part=1
> >>>
> >>> It seems that it complains about simulated kprobe's case.
> >>> In that case, cur->ainsn.xol_insn == NULL. The simulation should be done
> >>> in the kprobe context (which is a debug trap). I'm not sure the arm64
> >>> can cause NMI in that context, but if it happens and causes a fault,
> >>> it may cause a problem.
> >>>
> >>> So I think we can just ignore the fault on the simulated kprobes.
> >>>
> >>> To ensure that, you can just add:
> >>>
> >>> if (cur && !cur->ainsn.xol_insn)
> >>> return 0;
> >>>
> >>> at the entry of this function. (and remove redundant cur->ainsn.xol_insn check)
> >>
> >> Right, both cases:
> >>
> >> 1. single-step XOL
> >> 2. simulated
> >>
> >> have this problem and this patch fixed 1. 2 remains unchanged.
> >>
> >> Ideally we should fix both, but the simulated case seems more
> >> complicated, and at least we didn't make things worse for 2. So I wonder
> >> if we can analyze 2 more thoroughly and fix it in a separate patch.
> >
> > OK, but basically this fault handler is only for the SS XOL,
> > not for simulated one (as same as x86). So just skip the
> > simulated case is enough in this patch.
> > (Note that x86 also have simulated path, and that is not handled
> > by the fault handler)
>
> Hmm, what happens if the original PC is a simulated instruction that has
> a recoverable ex_table entry that handles potential page fault,
That should never happen. BPF trampoline code may populate extable
entries, but kprobe doesn't. For the simulated instruction, as far
as I can see, there are 2 operations
- simulate_ldr_literal
- simulate_ldrsw_literal
will involve the memory access and both are accessing PC-relative
kernel data, which should be mapped.
load_addr = addr + ldr_displacement(opcode);
...
set_x_reg(regs, xn, READ_ONCE(*(u64 *)load_addr));
this directly accessing the memory without using extable.
> and this
> PC also has an attached kprobe?
You meant that putting kprobes in simulate_* functions?
Those have __kprobes attribute, so kprobe can not probe it.
(It should use NOKPROBE_SYMBOL...)
> Then the simulated case should be able
> to enter kprobe_fault_handler()?
>
> .ex_table:
> insn foo, handler bar
>
> foo:
> LDR symbol # Load PC-relative. Simulated. Kprobe attached.
> ret
>
> When foo is called and the kprobe fires, it enters simulated path but
> then LDR triggers a page fault. It then enters kprobe_fault_handler() to
> fixup the PC. Then the fixup_exception() of the normal page fault finds
> the ex_table entry and this case is successfully handled?
To do that, you need to update the simulate_ldr* to use uaccess API
(_ASM_EXTABLE_UACCESS* macros) AND allow kprobes to probe those functions.
>
> Looks like this handler can be entered by simulated instructions? Or
> this is only theoretical and whoever arrange code like this should be
> fired immediately?
So the simulated instructions never cause fault, or if it causes a fault
that means the original code has a bug. (Of course there is room to
improve the bug message so that it decodes the address probed by kprobe
when a bug occurs.)
Thank you,
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults
2026-07-08 11:59 ` Masami Hiramatsu
@ 2026-07-08 12:12 ` Hongyan Xia
0 siblings, 0 replies; 23+ messages in thread
From: Hongyan Xia @ 2026-07-08 12:12 UTC (permalink / raw)
To: Masami Hiramatsu (Google)
Cc: Pu Hu, ada.coupriediaz, catalin.marinas, davem, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
On 7/8/2026 7:59 PM, Masami Hiramatsu wrote:
> On Wed, 8 Jul 2026 08:55:37 +0000
> Hongyan Xia <hongyan.xia@transsion.com> wrote:
>
>> On 7/8/2026 3:42 PM, Masami Hiramatsu wrote:
>>> On Wed, 8 Jul 2026 05:57:24 +0000
>>> Hongyan Xia <hongyan.xia@transsion.com> wrote:
>>>
>>>> Hi Masami,
>>>>
>>>> On 7/8/2026 8:46 AM, Masami Hiramatsu wrote:
>>>>> On Mon, 6 Jul 2026 08:36:48 +0000
>>>>> Pu Hu <hupu@transsion.com> wrote:
>>>>>
>>>>>> From: Pu Hu <hupu@transsion.com>
>>>>>>
>>>>>> kprobe_fault_handler() handles faults taken while kprobes is in
>>>>>> KPROBE_HIT_SS or KPROBE_REENTER state as faults caused by the
>>>>>> single-stepped instruction.
>>>>>>
>>>>>> That assumption is not always true. While a kprobe is preparing or
>>>>>> executing the out-of-line single-step instruction, other code may run
>>>>>> in that window. For example, perf or trace code can be invoked from the
>>>>>> debug exception path and may take a fault of its own. In that case the
>>>>>> fault did not happen on the kprobe XOL instruction, but the kprobe fault
>>>>>> handler may still try to recover it as a kprobe single-step fault.
>>>>>>
>>>>>> This can corrupt the exception recovery flow and leave the real fault to
>>>>>> be handled with a wrong PC. A typical reproducer is running simpleperf
>>>>>> with preemptirq tracepoints and dwarf callchains while a kprobe is
>>>>>> installed on a frequently executed kernel function.
>>>>>>
>>>>>> Fix this by handling faults in KPROBE_HIT_SS/KPROBE_REENTER only when
>>>>>> the faulting PC points at the current kprobe's XOL instruction. Faults
>>>>>> from any other PC are left to the normal fault handling path.
>>>>>>
>>>>>> This follows the same idea as the x86 fix in commit 6381c24cd6d5
>>>>>> ("kprobes/x86: Fix page-fault handling logic").
>>>>>>
>>>>>> Signed-off-by: Pu Hu <hupu@transsion.com>
>>>>>> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
>>>>>> ---
>>>>>> arch/arm64/kernel/probes/kprobes.c | 14 ++++++++++++++
>>>>>> 1 file changed, 14 insertions(+)
>>>>>>
>>>>>> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
>>>>>> index 43a0361a8bf0..e4d2852ce2fb 100644
>>>>>> --- a/arch/arm64/kernel/probes/kprobes.c
>>>>>> +++ b/arch/arm64/kernel/probes/kprobes.c
>>>>>> @@ -285,6 +285,20 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr)
>>>>>> switch (kcb->kprobe_status) {
>>>>>> case KPROBE_HIT_SS:
>>>>>> case KPROBE_REENTER:
>>>>>> + /*
>>>>>> + * A fault taken while a kprobe is single-stepping is not
>>>>>> + * necessarily caused by the instruction in the XOL slot. For
>>>>>> + * example, tracing or perf code running in this window may take
>>>>>> + * an unrelated fault.
>>>>>> + *
>>>>>> + * Handle the fault here only when the faulting PC is the XOL
>>>>>> + * instruction of the current kprobe. Otherwise let the normal
>>>>>> + * fault handling path deal with it.
>>>>>> + */
>>>>>> + if (cur->ainsn.xol_insn &&
>>>>>> + instruction_pointer(regs) != (unsigned long)cur->ainsn.xol_insn)
>>>>>> + break;
>>>>>
>>>>> Can you check Sashiko's comments[1]?
>>>>>
>>>>> [1] https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com?part=1
>>>>>
>>>>> It seems that it complains about simulated kprobe's case.
>>>>> In that case, cur->ainsn.xol_insn == NULL. The simulation should be done
>>>>> in the kprobe context (which is a debug trap). I'm not sure the arm64
>>>>> can cause NMI in that context, but if it happens and causes a fault,
>>>>> it may cause a problem.
>>>>>
>>>>> So I think we can just ignore the fault on the simulated kprobes.
>>>>>
>>>>> To ensure that, you can just add:
>>>>>
>>>>> if (cur && !cur->ainsn.xol_insn)
>>>>> return 0;
>>>>>
>>>>> at the entry of this function. (and remove redundant cur->ainsn.xol_insn check)
>>>>
>>>> Right, both cases:
>>>>
>>>> 1. single-step XOL
>>>> 2. simulated
>>>>
>>>> have this problem and this patch fixed 1. 2 remains unchanged.
>>>>
>>>> Ideally we should fix both, but the simulated case seems more
>>>> complicated, and at least we didn't make things worse for 2. So I wonder
>>>> if we can analyze 2 more thoroughly and fix it in a separate patch.
>>>
>>> OK, but basically this fault handler is only for the SS XOL,
>>> not for simulated one (as same as x86). So just skip the
>>> simulated case is enough in this patch.
>>> (Note that x86 also have simulated path, and that is not handled
>>> by the fault handler)
>>
>> Hmm, what happens if the original PC is a simulated instruction that has
>> a recoverable ex_table entry that handles potential page fault,
>
> That should never happen. BPF trampoline code may populate extable
> entries, but kprobe doesn't. For the simulated instruction, as far
> as I can see, there are 2 operations
> - simulate_ldr_literal
> - simulate_ldrsw_literal
> will involve the memory access and both are accessing PC-relative
> kernel data, which should be mapped.
>
> load_addr = addr + ldr_displacement(opcode);
> ...
> set_x_reg(regs, xn, READ_ONCE(*(u64 *)load_addr));
>
> this directly accessing the memory without using extable.
>
>
>> and this
>> PC also has an attached kprobe?
>
> You meant that putting kprobes in simulate_* functions?
> Those have __kprobes attribute, so kprobe can not probe it.
> (It should use NOKPROBE_SYMBOL...)
>
>> Then the simulated case should be able
>> to enter kprobe_fault_handler()?
>>
>> .ex_table:
>> insn foo, handler bar
>>
>> foo:
>> LDR symbol # Load PC-relative. Simulated. Kprobe attached.
>> ret
>>
>> When foo is called and the kprobe fires, it enters simulated path but
>> then LDR triggers a page fault. It then enters kprobe_fault_handler() to
>> fixup the PC. Then the fixup_exception() of the normal page fault finds
>> the ex_table entry and this case is successfully handled?
>
> To do that, you need to update the simulate_ldr* to use uaccess API
> (_ASM_EXTABLE_UACCESS* macros) AND allow kprobes to probe those functions.
I see. The kprobe code actually does not even allow this combination.
The arm64 arch_prepare_kprobe() will reject extable PCs. Sorry for the
noise.
>>
>> Looks like this handler can be entered by simulated instructions? Or
>> this is only theoretical and whoever arrange code like this should be
>> fired immediately?
>
> So the simulated instructions never cause fault, or if it causes a fault
> that means the original code has a bug. (Of course there is room to
> improve the bug message so that it decodes the address probed by kprobe
> when a bug occurs.)
If we rule out the edge case I came up with (which arm64 kprobe does not
even allow to happen), then other cases are indeed kernel bugs.
Thanks for walking us through some of the logic. Will fix things in v2.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [RFC 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping
2026-07-06 8:36 ` [RFC 0/3] " Pu Hu
2026-07-06 8:36 ` [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults Pu Hu
@ 2026-07-06 8:36 ` Pu Hu
2026-07-08 0:54 ` Masami Hiramatsu
2026-07-06 8:36 ` [RFC 3/3] selftests/kprobes: Add kprobe stress test for page fault handling Pu Hu
2026-07-08 0:55 ` [RFC 0/3] arm64: kprobes: Fix single-step fault and reentry handling Masami Hiramatsu
3 siblings, 1 reply; 23+ messages in thread
From: Pu Hu @ 2026-07-06 8:36 UTC (permalink / raw)
To: mhiramat
Cc: ada.coupriediaz, catalin.marinas, davem, Hongyan Xia, Pu Hu,
Jiazi Li, linux-arm-kernel, linux-kernel, linux-trace-kernel,
naveen, will, yang
From: Pu Hu <hupu@transsion.com>
A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This
can happen when tracing or perf code runs from the debug exception path
while the first kprobe is preparing or executing its out-of-line
single-step instruction.
Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable,
the same as a hit in KPROBE_REENTER. This is too strict. A hit in
KPROBE_HIT_SS is still a one-level reentry and can be handled by saving
the current kprobe state and setting up single-step for the new probe,
just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
The truly unrecoverable case is hitting another kprobe while already in
KPROBE_REENTER, because the reentry save area has already been consumed.
Move KPROBE_HIT_SS to the recoverable reentry cases and leave
KPROBE_REENTER as the unrecoverable nested reentry case.
This mirrors the x86 fix in commit 6a5022a56ac3
("kprobes/x86: Allow to handle reentered kprobe on single-stepping").
Signed-off-by: Pu Hu <hupu@transsion.com>
Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
---
arch/arm64/kernel/probes/kprobes.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index e4d2852ce2fb..764b2228cca0 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -240,10 +240,16 @@ static int __kprobes reenter_kprobe(struct kprobe *p,
switch (kcb->kprobe_status) {
case KPROBE_HIT_SSDONE:
case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SS:
+ /*
+ * A probe can be hit while another kprobe is preparing or
+ * executing its XOL single-step instruction. This is still a
+ * recoverable one-level reentry, so handle it in the same way as
+ * reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
+ */
kprobes_inc_nmissed_count(p);
setup_singlestep(p, regs, kcb, 1);
break;
- case KPROBE_HIT_SS:
case KPROBE_REENTER:
pr_warn("Failed to recover from reentered kprobes.\n");
dump_kprobe(p);
--
2.43.0
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping
2026-07-06 8:36 ` [RFC 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping Pu Hu
@ 2026-07-08 0:54 ` Masami Hiramatsu
2026-07-08 7:12 ` Hongyan Xia
0 siblings, 1 reply; 23+ messages in thread
From: Masami Hiramatsu @ 2026-07-08 0:54 UTC (permalink / raw)
To: Pu Hu
Cc: ada.coupriediaz, catalin.marinas, davem, Hongyan Xia, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
On Mon, 6 Jul 2026 08:36:49 +0000
Pu Hu <hupu@transsion.com> wrote:
> From: Pu Hu <hupu@transsion.com>
>
> A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This
> can happen when tracing or perf code runs from the debug exception path
> while the first kprobe is preparing or executing its out-of-line
> single-step instruction.
>
> Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable,
> the same as a hit in KPROBE_REENTER. This is too strict. A hit in
> KPROBE_HIT_SS is still a one-level reentry and can be handled by saving
> the current kprobe state and setting up single-step for the new probe,
> just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
>
> The truly unrecoverable case is hitting another kprobe while already in
> KPROBE_REENTER, because the reentry save area has already been consumed.
>
> Move KPROBE_HIT_SS to the recoverable reentry cases and leave
> KPROBE_REENTER as the unrecoverable nested reentry case.
>
> This mirrors the x86 fix in commit 6a5022a56ac3
> ("kprobes/x86: Allow to handle reentered kprobe on single-stepping").
Can you also check the Sashiko comment?
https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com?part=2
This seems indicating potentially brakage of reenter kprobes on arm64.
But is it possible to hit another kprobe while SS on arm64? It is
the same question about the previous one, can NMI happens during
the single stepping? (maybe yes, because it is non-maskable)
Anyway, for making it safer, we need to add saved_irqflags to prev_kprobe.
Thank you,
>
> Signed-off-by: Pu Hu <hupu@transsion.com>
> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
> ---
> arch/arm64/kernel/probes/kprobes.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
> index e4d2852ce2fb..764b2228cca0 100644
> --- a/arch/arm64/kernel/probes/kprobes.c
> +++ b/arch/arm64/kernel/probes/kprobes.c
> @@ -240,10 +240,16 @@ static int __kprobes reenter_kprobe(struct kprobe *p,
> switch (kcb->kprobe_status) {
> case KPROBE_HIT_SSDONE:
> case KPROBE_HIT_ACTIVE:
> + case KPROBE_HIT_SS:
> + /*
> + * A probe can be hit while another kprobe is preparing or
> + * executing its XOL single-step instruction. This is still a
> + * recoverable one-level reentry, so handle it in the same way as
> + * reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
> + */
> kprobes_inc_nmissed_count(p);
> setup_singlestep(p, regs, kcb, 1);
> break;
> - case KPROBE_HIT_SS:
> case KPROBE_REENTER:
> pr_warn("Failed to recover from reentered kprobes.\n");
> dump_kprobe(p);
> --
> 2.43.0
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping
2026-07-08 0:54 ` Masami Hiramatsu
@ 2026-07-08 7:12 ` Hongyan Xia
2026-07-08 14:12 ` Masami Hiramatsu
0 siblings, 1 reply; 23+ messages in thread
From: Hongyan Xia @ 2026-07-08 7:12 UTC (permalink / raw)
To: Masami Hiramatsu (Google), Pu Hu
Cc: ada.coupriediaz, catalin.marinas, davem, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
On 7/8/2026 8:54 AM, Masami Hiramatsu wrote:
> On Mon, 6 Jul 2026 08:36:49 +0000
> Pu Hu <hupu@transsion.com> wrote:
>
>> From: Pu Hu <hupu@transsion.com>
>>
>> A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This
>> can happen when tracing or perf code runs from the debug exception path
>> while the first kprobe is preparing or executing its out-of-line
>> single-step instruction.
>>
>> Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable,
>> the same as a hit in KPROBE_REENTER. This is too strict. A hit in
>> KPROBE_HIT_SS is still a one-level reentry and can be handled by saving
>> the current kprobe state and setting up single-step for the new probe,
>> just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
>>
>> The truly unrecoverable case is hitting another kprobe while already in
>> KPROBE_REENTER, because the reentry save area has already been consumed.
>>
>> Move KPROBE_HIT_SS to the recoverable reentry cases and leave
>> KPROBE_REENTER as the unrecoverable nested reentry case.
>>
>> This mirrors the x86 fix in commit 6a5022a56ac3
>> ("kprobes/x86: Allow to handle reentered kprobe on single-stepping").
>
> Can you also check the Sashiko comment?
>
> https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com?part=2
>
> This seems indicating potentially brakage of reenter kprobes on arm64.
> But is it possible to hit another kprobe while SS on arm64? It is
> the same question about the previous one, can NMI happens during
> the single stepping? (maybe yes, because it is non-maskable)
It is possible as this is what we hit. There are
preempt_enable/disable() calls during SS which can trigger perf events
sampling the user stack, which may then trigger page faults and send the
CPU into one more level of exception context. I believe this is what you
saw in 6a5022a56ac3?
> Anyway, for making it safer, we need to add saved_irqflags to prev_kprobe.
Yes, this seems to be a legit bug that needs to be fixed. Thank you.
> Thank you,
>
>>
>> Signed-off-by: Pu Hu <hupu@transsion.com>
>> Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
>> ---
>> arch/arm64/kernel/probes/kprobes.c | 8 +++++++-
>> 1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
>> index e4d2852ce2fb..764b2228cca0 100644
>> --- a/arch/arm64/kernel/probes/kprobes.c
>> +++ b/arch/arm64/kernel/probes/kprobes.c
>> @@ -240,10 +240,16 @@ static int __kprobes reenter_kprobe(struct kprobe *p,
>> switch (kcb->kprobe_status) {
>> case KPROBE_HIT_SSDONE:
>> case KPROBE_HIT_ACTIVE:
>> + case KPROBE_HIT_SS:
>> + /*
>> + * A probe can be hit while another kprobe is preparing or
>> + * executing its XOL single-step instruction. This is still a
>> + * recoverable one-level reentry, so handle it in the same way as
>> + * reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
>> + */
>> kprobes_inc_nmissed_count(p);
>> setup_singlestep(p, regs, kcb, 1);
>> break;
>> - case KPROBE_HIT_SS:
>> case KPROBE_REENTER:
>> pr_warn("Failed to recover from reentered kprobes.\n");
>> dump_kprobe(p);
>> --
>> 2.43.0
>>
>
>
> --
> Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping
2026-07-08 7:12 ` Hongyan Xia
@ 2026-07-08 14:12 ` Masami Hiramatsu
0 siblings, 0 replies; 23+ messages in thread
From: Masami Hiramatsu @ 2026-07-08 14:12 UTC (permalink / raw)
To: Hongyan Xia
Cc: Pu Hu, ada.coupriediaz, catalin.marinas, davem, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
On Wed, 8 Jul 2026 07:12:39 +0000
Hongyan Xia <hongyan.xia@transsion.com> wrote:
> On 7/8/2026 8:54 AM, Masami Hiramatsu wrote:
> > On Mon, 6 Jul 2026 08:36:49 +0000
> > Pu Hu <hupu@transsion.com> wrote:
> >
> >> From: Pu Hu <hupu@transsion.com>
> >>
> >> A kprobe can be hit while another kprobe is in KPROBE_HIT_SS state. This
> >> can happen when tracing or perf code runs from the debug exception path
> >> while the first kprobe is preparing or executing its out-of-line
> >> single-step instruction.
> >>
> >> Currently arm64 treats a kprobe hit in KPROBE_HIT_SS as unrecoverable,
> >> the same as a hit in KPROBE_REENTER. This is too strict. A hit in
> >> KPROBE_HIT_SS is still a one-level reentry and can be handled by saving
> >> the current kprobe state and setting up single-step for the new probe,
> >> just like reentry from KPROBE_HIT_ACTIVE or KPROBE_HIT_SSDONE.
> >>
> >> The truly unrecoverable case is hitting another kprobe while already in
> >> KPROBE_REENTER, because the reentry save area has already been consumed.
> >>
> >> Move KPROBE_HIT_SS to the recoverable reentry cases and leave
> >> KPROBE_REENTER as the unrecoverable nested reentry case.
> >>
> >> This mirrors the x86 fix in commit 6a5022a56ac3
> >> ("kprobes/x86: Allow to handle reentered kprobe on single-stepping").
> >
> > Can you also check the Sashiko comment?
> >
> > https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com?part=2
> >
> > This seems indicating potentially brakage of reenter kprobes on arm64.
> > But is it possible to hit another kprobe while SS on arm64? It is
> > the same question about the previous one, can NMI happens during
> > the single stepping? (maybe yes, because it is non-maskable)
>
> It is possible as this is what we hit. There are
> preempt_enable/disable() calls during SS which can trigger perf events
> sampling the user stack, which may then trigger page faults and send the
> CPU into one more level of exception context. I believe this is what you
> saw in 6a5022a56ac3?
Ah, it was more generic perf NMI case, not only preempt_enable/disable().
But anyway, the result is same.
> > Anyway, for making it safer, we need to add saved_irqflags to prev_kprobe.
>
> Yes, this seems to be a legit bug that needs to be fixed. Thank you.
OK, thanks!
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [RFC 3/3] selftests/kprobes: Add kprobe stress test for page fault handling
2026-07-06 8:36 ` [RFC 0/3] " Pu Hu
2026-07-06 8:36 ` [RFC 1/3] arm64: kprobes: Do not handle non-XOL faults as kprobe faults Pu Hu
2026-07-06 8:36 ` [RFC 2/3] arm64: kprobes: Allow reentering kprobes while single-stepping Pu Hu
@ 2026-07-06 8:36 ` Pu Hu
2026-07-08 0:55 ` [RFC 0/3] arm64: kprobes: Fix single-step fault and reentry handling Masami Hiramatsu
3 siblings, 0 replies; 23+ messages in thread
From: Pu Hu @ 2026-07-06 8:36 UTC (permalink / raw)
To: mhiramat
Cc: ada.coupriediaz, catalin.marinas, davem, Hongyan Xia, Pu Hu,
Jiazi Li, linux-arm-kernel, linux-kernel, linux-trace-kernel,
naveen, will, yang
From: Pu Hu <hupu@transsion.com>
Add a selftest that stresses kprobe handling around page fault paths.
This reproducer consists of:
- A kprobe module that probes folio_wait_bit_common()
- A userspace program that repeatedly triggers file-backed page faults
This is primarily intended to reproduce arm64 kprobe stability issues
related to XOL slot execution and fault handling during single-stepping.
Signed-off-by: Pu Hu <hupu@transsion.com>
---
tools/testing/selftests/kprobe/.gitignore | 2 +
tools/testing/selftests/kprobe/Makefile | 75 +++++++++++++
tools/testing/selftests/kprobe/fault_stress.c | 105 ++++++++++++++++++
.../selftests/kprobe/kprobe_folio_stress.c | 70 ++++++++++++
4 files changed, 252 insertions(+)
create mode 100644 tools/testing/selftests/kprobe/.gitignore
create mode 100644 tools/testing/selftests/kprobe/Makefile
create mode 100644 tools/testing/selftests/kprobe/fault_stress.c
create mode 100644 tools/testing/selftests/kprobe/kprobe_folio_stress.c
diff --git a/tools/testing/selftests/kprobe/.gitignore b/tools/testing/selftests/kprobe/.gitignore
new file mode 100644
index 000000000000..5fdb3ee02cf1
--- /dev/null
+++ b/tools/testing/selftests/kprobe/.gitignore
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+fault_stress
diff --git a/tools/testing/selftests/kprobe/Makefile b/tools/testing/selftests/kprobe/Makefile
new file mode 100644
index 000000000000..39f20de5370c
--- /dev/null
+++ b/tools/testing/selftests/kprobe/Makefile
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for kprobe fault stress test
+#
+
+ifneq ($(KERNELRELEASE),)
+
+# This part is used by kernel Kbuild when building the external module:
+#
+# make -C $(KDIR) M=$(CURDIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
+#
+obj-m += kprobe_folio_stress.o
+
+ccflags-y += -g -O0 -fno-omit-frame-pointer
+
+else
+
+# This part is used when running make directly in this directory.
+#
+# make KDIR=$BUILD_DIR ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE all
+#
+
+PWD := $(shell pwd)
+ARCH ?= arm64
+CROSS_COMPILE ?= aarch64-dumpstack-linux-gnu-
+KDIR ?= /lib/modules/$(shell uname -r)/build
+DEST_PATH ?= $(PWD)/out
+Q ?= @
+
+# Kernel module
+KP_MOD := kprobe_folio_stress
+KP_KO := $(KP_MOD).ko
+
+# Userspace test program
+UNIT_TEST := fault_stress
+UNIT_TEST_SRC := fault_stress.c
+
+# Userspace CFLAGS.
+# These options make userspace stacks easier to unwind.
+USER_CFLAGS := -static -g -O0 -fno-omit-frame-pointer -fasynchronous-unwind-tables
+USER_LIBS := -lm -lpthread
+
+.PHONY: all modules user install clean
+
+all: modules user
+
+modules:
+ $(Q)$(MAKE) -C $(KDIR) \
+ M=$(PWD) \
+ ARCH=$(ARCH) \
+ CROSS_COMPILE=$(CROSS_COMPILE) \
+ modules
+
+user:
+ $(Q)$(CROSS_COMPILE)gcc \
+ $(USER_CFLAGS) \
+ $(UNIT_TEST_SRC) \
+ -o $(UNIT_TEST) \
+ $(USER_LIBS)
+
+install: all
+ $(Q)mkdir -p $(DEST_PATH)
+ $(Q)cp -f $(KP_KO) $(DEST_PATH)/
+ $(Q)cp -f $(UNIT_TEST) $(DEST_PATH)/
+
+clean:
+ $(Q)$(MAKE) -C $(KDIR) \
+ M=$(PWD) \
+ ARCH=$(ARCH) \
+ CROSS_COMPILE=$(CROSS_COMPILE) \
+ clean
+ $(Q)rm -f $(UNIT_TEST)
+ $(Q)rm -rf $(DEST_PATH)
+
+endif
diff --git a/tools/testing/selftests/kprobe/fault_stress.c b/tools/testing/selftests/kprobe/fault_stress.c
new file mode 100644
index 000000000000..160e172dfa59
--- /dev/null
+++ b/tools/testing/selftests/kprobe/fault_stress.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * fault_stress.c - Userspace program to trigger file-backed page faults
+ *
+ * This program creates a file and repeatedly maps/unmaps it while
+ * accessing memory, triggering file-backed page faults. It's designed
+ * to work with kprobe-folio-stress.c to stress kprobe handling.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define FILE_SIZE (256UL * 1024 * 1024)
+#define NR_THREADS 8
+
+static void deep_call(int n)
+{
+ volatile char buf[4096];
+
+ memset((void *)buf, n, sizeof(buf));
+
+ if (n > 0)
+ deep_call(n - 1);
+ else
+ sched_yield();
+}
+
+static void *worker(void *arg)
+{
+ const char *path = arg;
+ int fd;
+ char *map;
+ unsigned long i;
+ volatile unsigned long sum = 0;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ return NULL;
+ }
+
+ map = mmap(NULL, FILE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ perror("mmap");
+ close(fd);
+ return NULL;
+ }
+
+ for (;;) {
+ /*
+ * Drop the pages backing this mapping from the current
+ * process. Subsequent accesses are more likely to trigger
+ * file-backed page faults again.
+ */
+ madvise(map, FILE_SIZE, MADV_DONTNEED);
+
+ for (i = 0; i < FILE_SIZE; i += 4096 * 17) {
+ sum += map[i];
+ deep_call(64);
+ }
+ }
+
+ munmap(map, FILE_SIZE);
+ close(fd);
+ return NULL;
+}
+
+int main(void)
+{
+ pthread_t th[NR_THREADS];
+ const char *path = "/tmp/fault_stress_file";
+ int fd;
+ int i;
+
+ fd = open(path, O_CREAT | O_RDWR, 0644);
+ if (fd < 0) {
+ perror("open file");
+ return 1;
+ }
+
+ if (ftruncate(fd, FILE_SIZE) < 0) {
+ perror("ftruncate");
+ close(fd);
+ return 1;
+ }
+
+ close(fd);
+
+ for (i = 0; i < NR_THREADS; i++)
+ pthread_create(&th[i], NULL, worker, (void *)path);
+
+ for (i = 0; i < NR_THREADS; i++)
+ pthread_join(th[i], NULL);
+
+ return 0;
+}
diff --git a/tools/testing/selftests/kprobe/kprobe_folio_stress.c b/tools/testing/selftests/kprobe/kprobe_folio_stress.c
new file mode 100644
index 000000000000..181a89f4c495
--- /dev/null
+++ b/tools/testing/selftests/kprobe/kprobe_folio_stress.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * kprobe_folio_stress.c - kprobe stress test for page fault handling
+ *
+ * This module installs a kprobe on folio_wait_bit_common() and is
+ * intended to be used together with fault_stress.c to stress kprobe
+ * handling around fault paths.
+ *
+ * Primary use case: reproduce arm64 kprobe stability issues related to
+ * XOL slot execution and fault handling during single-stepping.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/sched.h>
+#include <linux/atomic.h>
+#include <linux/ratelimit.h>
+#include <linux/kallsyms.h>
+
+
+static atomic64_t kp_hit_count = ATOMIC64_INIT(0);
+
+static int folio_wait_bit_common_handler(
+ struct kprobe *p, struct pt_regs *regs)
+{
+ unsigned long hit;
+
+ hit = atomic64_inc_return(&kp_hit_count);
+
+ pr_info("kp_folio: hit=%lu comm=%s tgid=%d tid=%d\n",
+ hit, current->comm, current->tgid, current->pid);
+
+ return 0;
+}
+
+static struct kprobe kp_folio_probe = {
+ .symbol_name = "folio_wait_bit_common",
+ .pre_handler = folio_wait_bit_common_handler,
+};
+
+static int __init kprobe_folio_init(void)
+{
+ int ret;
+
+ ret = register_kprobe(&kp_folio_probe);
+ if (ret < 0) {
+ pr_err("kp_folio: register_kprobe failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ pr_info("kp_folio: kprobe registered at %pS, addr=%px\n",
+ kp_folio_probe.addr, kp_folio_probe.addr);
+
+ return 0;
+}
+
+static void __exit kprobe_folio_exit(void)
+{
+ unregister_kprobe(&kp_folio_probe);
+ pr_info("kp_folio: kprobe unregistered, total hits=%lld\n",
+ atomic64_read(&kp_hit_count));
+}
+
+module_init(kprobe_folio_init);
+module_exit(kprobe_folio_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pu Hu <hupu@transsion.com>");
+MODULE_DESCRIPTION("kprobe stress test for folio_wait_bit_common");
--
2.43.0
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [RFC 0/3] arm64: kprobes: Fix single-step fault and reentry handling
2026-07-06 8:36 ` [RFC 0/3] " Pu Hu
` (2 preceding siblings ...)
2026-07-06 8:36 ` [RFC 3/3] selftests/kprobes: Add kprobe stress test for page fault handling Pu Hu
@ 2026-07-08 0:55 ` Masami Hiramatsu
3 siblings, 0 replies; 23+ messages in thread
From: Masami Hiramatsu @ 2026-07-08 0:55 UTC (permalink / raw)
To: Pu Hu
Cc: ada.coupriediaz, catalin.marinas, davem, Hongyan Xia, Jiazi Li,
linux-arm-kernel, linux-kernel, linux-trace-kernel, naveen, will,
yang
Hi, Pu,
Thanks for updating the series. But even if it just update
the signed-off-by, please update the version.
Also, can you check the Sashiko's comments?
https://sashiko.dev/#/patchset/20260706083636.159883-1-hupu%40transsion.com
Thank you,
On Mon, 6 Jul 2026 08:36:46 +0000
Pu Hu <hupu@transsion.com> wrote:
> From: Pu Hu <hupu@transsion.com>
>
> This series fixes two arm64 kprobes issues observed when running
> simpleperf with preemptirq tracepoints and dwarf callchains while a
> kprobe is active on a frequently executed kernel function.
>
> The crash happens in the kprobe debug exception path. While a kprobe is
> preparing or executing its XOL single-step instruction, perf/trace code
> can run in the same window. That code may either take a fault of its own
> or hit another kprobe.
>
> Patch 1 makes kprobe_fault_handler() handle a fault in
> KPROBE_HIT_SS/KPROBE_REENTER only when the faulting PC points at the
> current kprobe's XOL instruction. Otherwise the fault is left to the
> normal fault handling path.
>
> Patch 2 allows a kprobe hit in KPROBE_HIT_SS to be handled as a
> recoverable one-level reentry. Only a hit while already in
> KPROBE_REENTER remains unrecoverable.
>
> Patch 3 adds a kprobes selftest which registers a kprobe on a frequently
> executed filemap fault path and drives repeated file-backed page faults
> from userspace. This provides a smaller reproducer for validating the
> fault handling fix.
>
> This follows the same logic as the existing x86 fixes:
> 6381c24cd6d5 ("kprobes/x86: Fix page-fault handling logic")
> 6a5022a56ac3 ("kprobes/x86: Allow to handle reentered kprobe on single-stepping")
>
> Reproducer:
>
> simpleperf record -p <pid> -f 10000 \
> -e preemptirq:preempt_disable \
> -e preemptirq:preempt_enable \
> --duration 9 --call-graph dwarf \
> -o /data/local/tmp/perf.data
>
> The new selftest can be built from tools/testing/selftests/kprobe/ and
> used to exercise the page fault handling path with the test kprobe
> module loaded.
>
> Before this series, the crash reproduced frequently. With both patches
> applied, it was no longer reproduced in our testing.
>
>
> Pu Hu (3):
> arm64: kprobes: Do not handle non-XOL faults as kprobe faults
> arm64: kprobes: Allow reentering kprobes while single-stepping
> selftests/kprobes: Add kprobe stress test for page fault handling
>
> arch/arm64/kernel/probes/kprobes.c | 22 +++-
> tools/testing/selftests/kprobe/.gitignore | 2 +
> tools/testing/selftests/kprobe/Makefile | 75 +++++++++++++
> tools/testing/selftests/kprobe/fault_stress.c | 105 ++++++++++++++++++
> .../selftests/kprobe/kprobe_folio_stress.c | 70 ++++++++++++
> 5 files changed, 273 insertions(+), 1 deletion(-)
> create mode 100644 tools/testing/selftests/kprobe/.gitignore
> create mode 100644 tools/testing/selftests/kprobe/Makefile
> create mode 100644 tools/testing/selftests/kprobe/fault_stress.c
> create mode 100644 tools/testing/selftests/kprobe/kprobe_folio_stress.c
>
> --
> 2.43.0
>
--
Masami Hiramatsu (Google) <mhiramat@kernel.org>
^ permalink raw reply [flat|nested] 23+ messages in thread