* [PATCH 0/2] sparc64: fix relbranch_fixup() for BPr, FBfcc and FBPfcc
@ 2026-09-23 14:49 Danish Khateeb
2026-09-23 14:49 ` [PATCH 1/2] sparc64: kprobes: " Danish Khateeb
2026-09-23 14:54 ` [PATCH 2/2] sparc64: uprobes: " Danish Khateeb
0 siblings, 2 replies; 3+ messages in thread
From: Danish Khateeb @ 2026-09-23 14:49 UTC (permalink / raw)
To: David S. Miller, Andreas Larsson
Cc: Masami Hiramatsu, Oleg Nesterov, Peter Zijlstra, Allen Pais,
sparclinux, linux-trace-kernel, linux-kernel, Danish Khateeb
kprobes and uprobes on sparc64 single-step a copy of the probed
instruction, and each has a relbranch_fixup() that moves a taken
branch's target from the copy back to the probed code. Both only
recognize call, BPcc and Bicc. A taken BPr, FBfcc or FBPfcc therefore
continues next to the copy, which crashes the kernel for a kprobe and
kills the task with SIGILL for a uprobe. BPr is a common first
instruction (a brz,pn on an argument), so a probe on a function's entry
is enough to hit it.
Patch 1 fixes kprobes, where the check dates back to the start of git
history. Patch 2 fixes the uprobes copy of it. They are independent of
each other, and of my pending uprobes kmap_local_page() patch.
Testing, in QEMU sun4u with sparc64_defconfig plus PREEMPT,
DEBUG_PREEMPT, PROVE_LOCKING and DEBUG_ATOMIC_SLEEP. A static init puts
uprobes on seven branches in its own code: brz,pn at a function's entry,
brgz,pt, fbe,pt %fcc0 (FBPfcc) and fbe (FBfcc), plus be (Bicc),
be,pt %xcc (BPcc) and call as controls. Each function runs 200 times in
a child, alternating taken and not taken, and the delay slot's effect
and the path taken are both checked. It then puts a kprobe on
__se_sys_getcpu(), which starts with "brz,pn %o0", and calls getcpu()
200 times, half of them with a NULL cpu pointer.
- fe2ec83746e5 (v7.3-rc4+): the four new uprobe cases die with SIGILL
on their first taken branch, the controls pass, and the first taken
getcpu() under the kprobe crashes the kernel.
- Patch 1 only: the kprobe case passes with 200 hits; uprobes as before.
- Both patches: everything passes, with no new kernel warnings. Boot
has three DEBUG_PREEMPT "__this_cpu_read() in preemptible" reports
from iommu_tbl_range_alloc(), with or without these patches.
W=1 and sparse are clean for both files.
Danish Khateeb (2):
sparc64: kprobes: fix relbranch_fixup() for BPr, FBfcc and FBPfcc
sparc64: uprobes: fix relbranch_fixup() for BPr, FBfcc and FBPfcc
arch/sparc/kernel/kprobes.c | 13 ++++++++-----
arch/sparc/kernel/uprobes.c | 13 ++++++++-----
2 files changed, 16 insertions(+), 10 deletions(-)
base-commit: fe2ec83746e501645709761605c2464a44fd2929
--
2.55.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] sparc64: kprobes: fix relbranch_fixup() for BPr, FBfcc and FBPfcc
2026-09-23 14:49 [PATCH 0/2] sparc64: fix relbranch_fixup() for BPr, FBfcc and FBPfcc Danish Khateeb
@ 2026-09-23 14:49 ` Danish Khateeb
2026-09-23 14:54 ` [PATCH 2/2] sparc64: uprobes: " Danish Khateeb
1 sibling, 0 replies; 3+ messages in thread
From: Danish Khateeb @ 2026-09-23 14:49 UTC (permalink / raw)
To: David S. Miller, Andreas Larsson
Cc: Masami Hiramatsu, Oleg Nesterov, Peter Zijlstra, Allen Pais,
sparclinux, linux-trace-kernel, linux-kernel, Danish Khateeb,
stable
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@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Danish Khateeb <danishkhateeb03@gmail.com>
---
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
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 2/2] sparc64: uprobes: fix relbranch_fixup() for BPr, FBfcc and FBPfcc
2026-09-23 14:49 [PATCH 0/2] sparc64: fix relbranch_fixup() for BPr, FBfcc and FBPfcc Danish Khateeb
2026-09-23 14:49 ` [PATCH 1/2] sparc64: kprobes: " Danish Khateeb
@ 2026-09-23 14:54 ` Danish Khateeb
1 sibling, 0 replies; 3+ messages in thread
From: Danish Khateeb @ 2026-09-23 14:54 UTC (permalink / raw)
To: David S. Miller, Andreas Larsson
Cc: Masami Hiramatsu, Oleg Nesterov, Peter Zijlstra, Allen Pais,
sparclinux, linux-trace-kernel, linux-kernel, Danish Khateeb,
stable
uprobes single-step a copy of the probed instruction in an XOL slot.
When the copy is a taken PC-relative branch, its target is relative to
the slot, and relbranch_fixup() moves it back to the probed code. Like
its kprobes counterpart, it only recognizes call, BPcc and Bicc. After a
taken BPr (brz, brnz, ...), FBfcc or FBPfcc, the task continues at the
slot's address plus the branch displacement, inside the XOL page, and
dies with SIGILL.
GCC often emits a BPr as a function's first instruction, so a plain
function-entry uprobe can kill the probed program. In QEMU sun4u, with a
uprobe on a "brz,pn %o0, 1f", the first call with %o0 == 0 ends with
init: potentially unexpected fatal signal 4.
TSTATE: 0000000082000203 TPC: fffffffbffffe01c TNPC: fffffffbffffe020
where the task's [uprobes] mapping is fffffffbffffe000-fffffffc00000000.
FBfcc and FBPfcc behave the same.
Add the missing branch formats, as for kprobes.
Fixes: e8f4aa6087fa ("sparc64:Support User Probes for sparc")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Danish Khateeb <danishkhateeb03@gmail.com>
---
arch/sparc/kernel/uprobes.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/arch/sparc/kernel/uprobes.c b/arch/sparc/kernel/uprobes.c
index c8cac64e9988..c8ba033427ba 100644
--- a/arch/sparc/kernel/uprobes.c
+++ b/arch/sparc/kernel/uprobes.c
@@ -97,12 +97,15 @@ static unsigned long relbranch_fixup(u32 insn, struct uprobe_task *utask,
if (regs->tnpc == regs->tpc + 0x4UL)
return utask->autask.saved_tnpc + 0x4UL;
- /* 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 real_pc = (unsigned long) utask->vaddr;
unsigned long ixol_addr = utask->xol_vaddr;
--
2.55.0
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-23 14:54 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-23 14:49 [PATCH 0/2] sparc64: fix relbranch_fixup() for BPr, FBfcc and FBPfcc Danish Khateeb
2026-09-23 14:49 ` [PATCH 1/2] sparc64: kprobes: " Danish Khateeb
2026-09-23 14:54 ` [PATCH 2/2] sparc64: uprobes: " Danish Khateeb
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®