[RFC v2 3/3] arm64: kprobes: Save and restore saved_irqflag in prev_kprobe
From: Pu Hu <hidden>
Date: 2026-07-09 14:22:31
Also in:
lkml
Subsystem:
arm64 port (aarch64 architecture), the rest · Maintainers:
Catalin Marinas, Will Deacon, Linus Torvalds
From: Pu Hu <redacted> When a kprobe is reentered from KPROBE_HIT_SS state (allowed by the previous patch), setup_singlestep() for the nested kprobe calls kprobes_save_local_irqflag(), overwriting kcb->saved_irqflag with the currently masked DAIF value. The outer kprobe's original DAIF state is lost. When the nested kprobe completes, restore_previous_kprobe() brings back the outer kprobe's kp and status, but saved_irqflag still contains the nested kprobe's value. When the outer kprobe's single-step eventually finishes, kprobes_restore_local_irqflag() applies the wrong DAIF mask, leaving interrupts permanently disabled. Fix this by extending struct prev_kprobe with a saved_irqflag field, and saving/restoring it alongside kp and status. This ensures the outer kprobe's original interrupt state is preserved across reentry. Signed-off-by: Pu Hu <redacted> Signed-off-by: Hongyan Xia <redacted> --- arch/arm64/include/asm/kprobes.h | 6 ++++++ arch/arm64/kernel/probes/kprobes.c | 15 +++++++++++++++ 2 files changed, 21 insertions(+)
diff --git a/arch/arm64/include/asm/kprobes.h b/arch/arm64/include/asm/kprobes.h
index f2782560647b..35ce2c94040e 100644
--- a/arch/arm64/include/asm/kprobes.h
+++ b/arch/arm64/include/asm/kprobes.h@@ -26,6 +26,12 @@ struct prev_kprobe { struct kprobe *kp; unsigned int status; + + /* + * The original DAIF state of the outer kprobe, saved here before + * a nested kprobe overwrites kcb->saved_irqflag during reentry. + */ + unsigned long saved_irqflag; }; /* per-cpu kprobe control block */
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index 2ca5916eca2f..4e0efad5caf2 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c@@ -174,12 +174,27 @@ static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) { kcb->prev_kprobe.kp = kprobe_running(); kcb->prev_kprobe.status = kcb->kprobe_status; + + /* + * Save the outer kprobe's original DAIF flags before the nested + * kprobe calls kprobes_save_local_irqflag() and overwrites + * kcb->saved_irqflag. Without this, the outer kprobe will restore + * the wrong DAIF state and leave interrupts permanently masked. + */ + kcb->prev_kprobe.saved_irqflag = kcb->saved_irqflag; } static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) { __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); kcb->kprobe_status = kcb->prev_kprobe.status; + + /* + * Restore the outer kprobe's saved_irqflag so that when its + * single-step completes, kprobes_restore_local_irqflag() uses + * the correct original DAIF value. + */ + kcb->saved_irqflag = kcb->prev_kprobe.saved_irqflag; } static void __kprobes set_current_kprobe(struct kprobe *p)
--
2.43.0