[PATCH 1/2] sparc64: kprobes: fix relbranch_fixup() for BPr, FBfcc and FBPfcc
From: Danish Khateeb
Date: Wed Sep 23 2026 - 13:00:34 EST
A kprobe single-steps a copy of the probed instruction in
p->ainsn.insn[]. When the copy is a taken PC-relative branch, its target
is relative to the copy, and relbranch_fixup() moves it back to the
probed code. It only recognizes call, BPcc and Bicc, though. For a taken
BPr (brz, brnz, ...), FBfcc or FBPfcc it keeps the target as is, and the
kernel continues at the copy's address plus the branch displacement.
GCC often starts a function with a BPr on an argument. For example,
__se_sys_getcpu() begins with "brz,pn %o0". With a kprobe on it
("p:kprobes/kgetcpu __se_sys_getcpu" in kprobe_events), the first
getcpu(NULL, NULL, NULL) crashes the kernel in QEMU sun4u:
init(1): Kernel illegal instruction [#1]
TSTATE: 0000004411001603 TPC: fffff800048d63c0 TNPC: fffff8000492631c
Kernel panic - not syncing: Fatal exception
Add the missing branch formats. BPr is matched with bit 28 clear. The
CBcond instructions of newer CPUs share its op2 value, but they have no
delay slot, so a taken one never reaches the single-step breakpoint and
this function.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Danish Khateeb <danishkhateeb03@xxxxxxxxx>
---
arch/sparc/kernel/kprobes.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/arch/sparc/kernel/kprobes.c b/arch/sparc/kernel/kprobes.c
index 191bbaca9921..9a26c57c8d33 100644
--- a/arch/sparc/kernel/kprobes.c
+++ b/arch/sparc/kernel/kprobes.c
@@ -207,12 +207,15 @@ static unsigned long __kprobes relbranch_fixup(u32 insn, struct kprobe *p,
if (regs->tnpc == regs->tpc + 0x4UL)
return real_pc + 0x8UL;
- /* The three cases are call, branch w/prediction,
- * and traditional branch.
+ /* The cases are call and the branches with a PC-relative
+ * displacement.
*/
- if ((insn & 0xc0000000) == 0x40000000 ||
- (insn & 0xc1c00000) == 0x00400000 ||
- (insn & 0xc1c00000) == 0x00800000) {
+ if ((insn & 0xc0000000) == 0x40000000 || /* call */
+ (insn & 0xc1c00000) == 0x00400000 || /* BPcc */
+ (insn & 0xc1c00000) == 0x00800000 || /* Bicc */
+ (insn & 0xd1c00000) == 0x00c00000 || /* BPr */
+ (insn & 0xc1c00000) == 0x01400000 || /* FBPfcc */
+ (insn & 0xc1c00000) == 0x01800000) { /* FBfcc */
unsigned long ainsn_addr;
ainsn_addr = (unsigned long) &p->ainsn.insn[0];
--
2.55.0