[PATCH] LoongArch: kprobes: Avoid preempt count underflow without probe
Jérémy Jean <[email protected]>
| Newsgroups | dev.linux.lists.loongarch,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
LoongArch uses break 11 for the breakpoint placed after an instruction
that Kprobes executes out of line. Since userspace can issue the same
break instruction, do_bp() can reach kprobe_singlestep_handler() when
there is no current probe.
The handler returns false in this case, but first calls
preempt_enable_no_resched(). The corresponding preempt_disable() is done
by kprobe_breakpoint_handler() on a real Kprobe hit, so it has not run
here. As a result, an ordinary userspace breakpoint underflows the
current task's preempt count.
This also makes in_interrupt() return true until the task schedules. One
visible consequence is socket cgroup attribution: cgroup_sk_alloc()
treats the allocation as interrupt context and assigns the socket to the
root cgroup. A socket opened from the SIGTRAP handler can then avoid a
BPF_CGROUP_INET_SOCK_CREATE policy attached to the task's own cgroup.
Return as soon as kprobe_running() reports no active probe.
The same check appeared in [PATCH v10 2/4] of the original LoongArch
Kprobes series, but was dropped before the feature reached mainline.
Link: https://patchew.org/linux/1670575981-14389-1-git-send-email-yangtiezhu%40loongson.cn/1670575981-14389-3-git-send-email-yangtiezhu%40loongson.cn/
Fixes: 6d4cc40fb5f5 ("LoongArch: Add kprobes support")
Assisted-by: Codex:gpt-5
Signed-off-by: Jérémy Jean <[email protected]>
---
arch/loongarch/kernel/kprobes.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c
index 1985ed30dd16f..ddfefea174727 100644
--- a/arch/loongarch/kernel/kprobes.c
+++ b/arch/loongarch/kernel/kprobes.c
@@ -275,6 +275,9 @@ bool kprobe_singlestep_handler(struct pt_regs *regs)
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
unsigned long addr = instruction_pointer(regs);
+ if (!cur)
+ return false;
+
if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) &&
((unsigned long)&cur->ainsn.insn[1] == addr)) {
restore_local_irqflag(kcb, regs);
--
2.47.3