* [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage
@ 2026-08-31 7:15 Saket Kumar Bhaskar
2026-08-31 7:15 ` [PATCH v13 1/8] powerpc64/bpf: fix compare instruction emitted for tailcall Saket Kumar Bhaskar
` (7 more replies)
0 siblings, 8 replies; 13+ messages in thread
From: Saket Kumar Bhaskar @ 2026-08-31 7:15 UTC (permalink / raw)
To: bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99
From: Abhishek Dubey <adubey@linux.ibm.com>
The verifier selftest validates JITed instructions by matching expected
disassembly output. The first two patches fix issues in powerpc instruction
disassembly that were causing test flow failures. The fix is common for
64-bit & 32-bit powerpc. Add support for the powerpc-specific "__powerpc64"
architecture tag in the third patch, enabling proper test filtering in
verifier test files. Introduce verifier testcases for tailcalls on powerpc64.
The first patch in series is fix patch, correcting memory alignment with
8-byte boundary for long branch address field. The subsequent patches
enables verifier selftests on powerpc. The fifth patch in the series fixes
incorrect comparator usage for comparing tailcall info with tailcall
threshold. The last two patches fix JIT buffer overflow for large BPF progs
and private stack memory leak (identified by bot during reviews).
Issue Details:
--------------
The Long branch stub in the trampoline implementation[1] provides
flexibility to handles short as well as long branch distance to
actual trampoline. Whereas, the 8 bytes long dummy_tramp_addr field
sitting before long branch stub leads to failure when enabling
verifier based seltest for ppc64.
The verifier selftests require disassembing the final jited image
to get native instructions. Later the disassembled instruction
sequence is matched against sequence of instructions provided in
test-file under __jited() wrapper. The final jited image contains
Out-of-line stub and Long branch stub as part of epilogue jitting
for a bpf program. The 8 bytes space for dummy_tramp is sandwiched
between both above mentioned stubs. These 8 bytes contain memory
address of dummy trampoline during trampoline invocation which don't
correspond to any powerpc instructions. So, disassembly fails
resulting in failure of verifier selftests.
The following code snippet shows the problem with current arrangement
made for dummy_tramp_addr.
/* Out-of-line stub */
mflr r0
[b|bl] tramp
mtlr r0 //only with OOL
b bpf_func + 4
/* Long branch stub */
.long <dummy_tramp_addr> <---Invalid bytes sequence, disassembly fails
mflr r11
bcl 20,31,$+4
mflr r12
ld r12, -8-SZL(r12)
mtctr r12
mtlr r11 //retain ftrace ABI
bctr
Consider test program binary of size 112 bytes:
0: 00000060 10004de8 00002039 f8ff21f9 81ff21f8 7000e1fb 3000e13b
28: 3000e13b 2a006038 f8ff7ff8 00000039 7000e1eb 80002138 7843037d
56: 2000804e a602087c 00000060 a603087c bcffff4b c0341d00 000000c0
84: a602687d 05009f42 a602887d f0ff8ce9 a603897d a603687d 2004804e
Disassembly output of above binary for ppc64le:
pc:0 left:112 00 00 00 60 : nop
pc:4 left:108 10 00 4d e8 : ld 2, 16(13)
pc:8 left:104 00 00 20 39 : li 9, 0
pc:12 left:100 f8 ff 21 f9 : std 9, -8(1)
pc:16 left:96 81 ff 21 f8 : stdu 1, -128(1)
pc:20 left:92 70 00 e1 fb : std 31, 112(1)
pc:24 left:88 30 00 e1 3b : addi 31, 1, 48
pc:28 left:84 30 00 e1 3b : addi 31, 1, 48
pc:32 left:80 2a 00 60 38 : li 3, 42
pc:36 left:76 f8 ff 7f f8 : std 3, -8(31)
pc:40 left:72 00 00 00 39 : li 8, 0
pc:44 left:68 70 00 e1 eb : ld 31, 112(1)
pc:48 left:64 80 00 21 38 : addi 1, 1, 128
pc:52 left:60 78 43 03 7d : mr 3, 8
pc:56 left:56 20 00 80 4e : blr
pc:60 left:52 a6 02 08 7c : mflr 0
pc:64 left:48 00 00 00 60 : nop
pc:68 left:44 a6 03 08 7c : mtlr 0
pc:72 left:40 bc ff ff 4b : b .-68
pc:76 left:36 c0 34 1d 00 :
...
Failure log:
Can't disasm instruction at offset 76: c0 34 1d 00 00 00 00 c0 a6 02 68 7d 05 00 9f 42
--------------------------------------
Observation:
Can't disasm instruction at offset 76 as this address has
".long <dummy_tramp_addr>" (0xc0341d00000000c0)
But valid instructions follow at offset 84 onwards.
Move the long branch address space to the bottom of the long
branch stub. This allows uninterrupted disassembly until the
last 8 bytes. Exclude these last bytes from the overall
program length to prevent failure in assembly generation.
Following is disassembler output for same test program with moved down
dummy_tramp_addr field:
.....
.....
pc:68 left:44 a6 03 08 7c : mtlr 0
pc:72 left:40 bc ff ff 4b : b .-68
pc:76 left:36 a6 02 68 7d : mflr 11
pc:80 left:32 05 00 9f 42 : bcl 20, 31, .+4
pc:84 left:28 a6 02 88 7d : mflr 12
pc:88 left:24 14 00 8c e9 : ld 12, 20(12)
pc:92 left:20 a6 03 89 7d : mtctr 12
pc:96 left:16 a6 03 68 7d : mtlr 11
pc:100 left:12 20 04 80 4e : bctr
pc:104 left:8 c0 34 1d 00 :
Failure log:
Can't disasm instruction at offset 104: c0 34 1d 00 00 00 00 c0
---------------------------------------
Disassembly logic can truncate at 104, ignoring last 8 bytes.
Update the dummy_tramp_addr field offset calculation from the end
of the program to reflect its new location, for bpf_arch_text_poke()
to update the actual trampoline's address in this field.
[1] https://lore.kernel.org/all/20241030070850.1361304-18-hbathini@linux.ibm.com
v12->v13:
Moved patch 4 and 5 before 1.
Modified comments in code.
v11->v12:
Fix build failure for ppc32
Rearranged the order of patches as suggested by Hari
v10->v11:
Moved function prototype change from patch 2 to patch 1
Fixed commit message for patch 1 and patch 4
Fixed extable entry generation condition
Added reviewed and acked tags by Hari
v9->v10:
Modified expected JIT instruction in selftest for
CONFIG_PPC_KERNEL_PCREL incase of PPC_LI64 instruction.
Fix for stale entries in exception table
Update ARCH_POWERPC64 enum value to 0x40 to resolve rebase
conflict with ARCH_LOONGARCH (0x20).
v8->v9:
Dynamic pass handling until code keeps shrinking
Fix private stack memory leak
v7->v8:
Fixed bot identified issues of alt_exit_addr and BPF_EXIT
Fixed 32-bit ppc function signature mismatch
v6->v7:
Fixed JIT buffer overflow in case of large BPF progs
Addressed remaining bot comments
v5->v6:
Changed alignment NOP emittion dependency on fimage layout
Adjust tail truncate length for 32-bit ppc
Addressed few minor bot comments
v4->v5:
Handled alignment NOP emit logic and corresponding stub offsets
Handled image buffer overflow problem in last pass
Above changes took care of other bot reviews
Included LLVMDisposeMessage() for graceful freeing
Adjusted parameters in bpf_jit_build_fentry_stubs for ppc32
Adjusted expected JIT inst. in tailcall test for
CONFIG_PPC_KERNEL_PCREL config
Added fix patch at last for inaccurate use of cmplwi inst.
v3->v4:
Changed logic for emitting alignment NOP
v2->v3:
Removed fixed NOP from bottom of long branch stub
Rebased on top of bpf-next
v1->v2:
Added fix-patch to correct memory alignment in-place
Moved the optional alignmnet NOP before OOL stub
[v1]: https://lore.kernel.org/bpf/20260225013627.22098-1-adubey@linux.ibm.com
[v2]: https://lore.kernel.org/bpf/20260403004011.44417-1-adubey@linux.ibm.com
[v3]: https://lore.kernel.org/bpf/20260411221413.44304-1-adubey@linux.ibm.com
[v4]: https://lore.kernel.org/bpf/20260517214043.12975-1-adubey@linux.ibm.com
[v5]: https://lore.kernel.org/bpf/20260519233812.18787-1-adubey@linux.ibm.com
[v6]: https://lore.kernel.org/bpf/20260529015855.364704-1-adubey@linux.ibm.com
[v7]: https://lore.kernel.org/bpf/20260611153826.31187-1-adubey@linux.ibm.com
[v8]: https://lore.kernel.org/bpf/20260616164741.32252-1-adubey@linux.ibm.com
[v9]: https://lore.kernel.org/bpf/20260623231411.6216-1-adubey@linux.ibm.com/
[v10]: https://lore.kernel.org/all/cover.1785871099.git.skb99@linux.ibm.com/
[v11]: https://lore.kernel.org/all/cover.1785871099.git.skb99@linux.ibm.com/#r
[v12]: https://lore.kernel.org/all/cover.1786611077.git.skb99@linux.ibm.com/
Abhishek Dubey (8):
powerpc64/bpf: fix compare instruction emitted for tailcall
powerpc64/bpf: fix percpu private stack leak on JIT failure
powerpc/bpf: fix buffer overflow in JIT for large BPF programs
powerpc/bpf: fix alignment of long branch trampoline address
powerpc/bpf: Move out dummy_tramp_addr after Long branch stub
selftests/bpf: Fix powerpc JIT disassembly failure
selftests/bpf: Enable verifier selftest for powerpc64
selftests/bpf: Add tailcall verifier selftest for powerpc64
arch/powerpc/net/bpf_jit.h | 20 +++-
arch/powerpc/net/bpf_jit_comp.c | 100 +++++++++++++-----
arch/powerpc/net/bpf_jit_comp32.c | 7 +-
arch/powerpc/net/bpf_jit_comp64.c | 15 +--
.../selftests/bpf/jit_disasm_helpers.c | 25 ++++-
tools/testing/selftests/bpf/progs/bpf_misc.h | 1 +
.../bpf/progs/verifier_tailcall_jit.c | 78 ++++++++++++++
tools/testing/selftests/bpf/test_loader.c | 5 +
8 files changed, 211 insertions(+), 40 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v13 1/8] powerpc64/bpf: fix compare instruction emitted for tailcall
2026-08-31 7:15 [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage Saket Kumar Bhaskar
@ 2026-08-31 7:15 ` Saket Kumar Bhaskar
2026-08-31 7:15 ` [PATCH v13 2/8] powerpc64/bpf: fix percpu private stack leak on JIT failure Saket Kumar Bhaskar
` (6 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Saket Kumar Bhaskar @ 2026-08-31 7:15 UTC (permalink / raw)
To: bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99
From: Abhishek Dubey <adubey@linux.ibm.com>
The tail_call_info field can contain either a scalar counter
value or a 64-bit pointer to the counter, using a 32-bit
compare (cmplwi) only checks the lower 32 bits, which can lead
to incorrect comparisions when location of counter is near 4GB
boundary. Use instruction cmpldi/cmplwi for accurate comparision
in corresponding cases.
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/bpf/20260517191450.85AE6C2BCB8@smtp.kernel.org/
Fixes: 2ed2d8f6fb38 ("powerpc64/bpf: Support tailcalls with subprogs")
Signed-off-by: Abhishek Dubey <adubey@linux.ibm.com>
Signed-off-by: Saket Kumar Bhaskar <skb99@linux.ibm.com>
Tested-by: Yeswanth Krishna Tellakula <yeswanth@linux.ibm.com>
Acked-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/net/bpf_jit.h | 6 ++++++
arch/powerpc/net/bpf_jit_comp.c | 2 +-
arch/powerpc/net/bpf_jit_comp64.c | 8 ++++----
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h
index f32de8704d4d..35015d7ecb76 100644
--- a/arch/powerpc/net/bpf_jit.h
+++ b/arch/powerpc/net/bpf_jit.h
@@ -188,6 +188,12 @@ struct codegen_context {
#define bpf_to_ppc(r) (ctx->b2p[r])
+#ifdef CONFIG_PPC64
+#define PPC_RAW_CMPLLI(a, i) PPC_RAW_CMPLDI(a, i)
+#else
+#define PPC_RAW_CMPLLI(a, i) PPC_RAW_CMPLWI(a, i)
+#endif
+
#ifdef CONFIG_PPC32
#define BPF_FIXUP_LEN 3 /* Three instructions => 12 bytes */
#else
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 7b07b43575f1..a875521ff904 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -739,7 +739,7 @@ static void bpf_trampoline_setup_tail_call_info(u32 *image, struct codegen_conte
* Setting the tail_call_info in trampoline's frame
* depending on if previous frame had value or reference.
*/
- EMIT(PPC_RAW_CMPLWI(_R3, MAX_TAIL_CALL_CNT));
+ EMIT(PPC_RAW_CMPLLI(_R3, MAX_TAIL_CALL_CNT));
PPC_BCC_CONST_SHORT(COND_GT, 8);
EMIT(PPC_RAW_ADDI(_R3, _R4, -BPF_PPC_TAILCALL));
diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c
index dab106cae22b..59302cabc466 100644
--- a/arch/powerpc/net/bpf_jit_comp64.c
+++ b/arch/powerpc/net/bpf_jit_comp64.c
@@ -276,7 +276,7 @@ void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx)
*/
EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_2), _R1, 0));
EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_1), bpf_to_ppc(TMP_REG_2), -(BPF_PPC_TAILCALL)));
- EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
+ EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
PPC_BCC_CONST_SHORT(COND_GT, 8);
EMIT(PPC_RAW_ADDI(bpf_to_ppc(TMP_REG_1), bpf_to_ppc(TMP_REG_2),
-(BPF_PPC_TAILCALL)));
@@ -662,7 +662,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
PPC_BCC_SHORT(COND_GE, out);
EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_1), _R1, bpf_jit_stack_tailcallinfo_offset(ctx)));
- EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
+ EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
PPC_BCC_CONST_SHORT(COND_LE, 8);
/* dereference TMP_REG_1 */
@@ -672,7 +672,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
* if (tail_call_info == MAX_TAIL_CALL_CNT)
* goto out;
*/
- EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
+ EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_1), MAX_TAIL_CALL_CNT));
PPC_BCC_SHORT(COND_EQ, out);
/*
@@ -707,7 +707,7 @@ static int bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32 o
* tail_call_info.
*/
EMIT(PPC_RAW_LD(bpf_to_ppc(TMP_REG_2), _R1, bpf_jit_stack_tailcallinfo_offset(ctx)));
- EMIT(PPC_RAW_CMPLWI(bpf_to_ppc(TMP_REG_2), MAX_TAIL_CALL_CNT));
+ EMIT(PPC_RAW_CMPLDI(bpf_to_ppc(TMP_REG_2), MAX_TAIL_CALL_CNT));
PPC_BCC_CONST_SHORT(COND_GT, 8);
/* First get address of tail_call_info */
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v13 2/8] powerpc64/bpf: fix percpu private stack leak on JIT failure
2026-08-31 7:15 [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage Saket Kumar Bhaskar
2026-08-31 7:15 ` [PATCH v13 1/8] powerpc64/bpf: fix compare instruction emitted for tailcall Saket Kumar Bhaskar
@ 2026-08-31 7:15 ` Saket Kumar Bhaskar
2026-08-31 7:16 ` [PATCH v13 3/8] powerpc/bpf: fix buffer overflow in JIT for large BPF programs Saket Kumar Bhaskar
` (5 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Saket Kumar Bhaskar @ 2026-08-31 7:15 UTC (permalink / raw)
To: bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99
From: Abhishek Dubey <adubey@linux.ibm.com>
The existing conditional statement in bpf_int_jit_compile() frees the
percpu private stack at out_addrs only when the image buffer was never
allocated.
If bpf_jit_build_body() fails during a code-generation pass, the
image buffer has already been allocated, so !image is false and the
percpu stack is not freed.
Because JIT compilation failed, fp->jited remains at 0. The subsequent
bpf_jit_free() path only frees priv_stack_ptr when fp->jited is set, so
freeing is skipped here too, leaking the percpu allocation.
Fix implements freeing the private stack whenever fp->jited was not set,
i.e. compilation did not succeed, instead of keying off !image. !fp->jited
already covers the !image case, since image is only NULL on early-failure
paths where fp->jited is likewise 0.
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/bpf/20260616135426.A06B71F000E9@smtp.kernel.org
Fixes: 156d985123b6 ("powerpc64/bpf: Implement JIT support for private stack")
Cc: stable@vger.kernel.org
Signed-off-by: Abhishek Dubey <adubey@linux.ibm.com>
Signed-off-by: Saket Kumar Bhaskar <skb99@linux.ibm.com>
Tested-by: Yeswanth Krishna Tellakula <yeswanth@linux.ibm.com>
Acked-by: Hari Bathini <hbathini@linux.ibm.com>
---
arch/powerpc/net/bpf_jit_comp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index a875521ff904..8f7501954d9f 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -357,7 +357,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
(void *)fimage + FUNCTION_DESCR_SIZE);
out_addrs:
- if (!image && priv_stack_ptr) {
+ if (!fp->jited && priv_stack_ptr) {
fp->aux->priv_stack_ptr = NULL;
free_percpu(priv_stack_ptr);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v13 3/8] powerpc/bpf: fix buffer overflow in JIT for large BPF programs
2026-08-31 7:15 [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage Saket Kumar Bhaskar
2026-08-31 7:15 ` [PATCH v13 1/8] powerpc64/bpf: fix compare instruction emitted for tailcall Saket Kumar Bhaskar
2026-08-31 7:15 ` [PATCH v13 2/8] powerpc64/bpf: fix percpu private stack leak on JIT failure Saket Kumar Bhaskar
@ 2026-08-31 7:16 ` Saket Kumar Bhaskar
2026-08-31 8:24 ` bot+bpf-ci
2026-08-31 7:16 ` [PATCH v13 4/8] powerpc/bpf: fix alignment of long branch trampoline address Saket Kumar Bhaskar
` (4 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Saket Kumar Bhaskar @ 2026-08-31 7:16 UTC (permalink / raw)
To: bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99
From: Abhishek Dubey <adubey@linux.ibm.com>
During size calculation in pass-0, exit_addr is 0 since addrs[fp->len]
is not yet populated. bpf_jit_emit_exit_insn() treats a zero exit_addr
as in-range and skips bpf_jit_build_epilogue(), so the alternate inline
epilogue instructions are not counted in alloclen.
In later passes, if the real exit_addr falls outside the 32MB branch
range, the full inline epilogue is emitted into the already-allocated
buffer, writing past its end and corrupting adjacent memory.
Fix by ensuring exit_addr is non-zero before treating it as in-range,
so pass-0 always falls through to bpf_jit_build_epilogue() and
conservatively accounts for all epilogue instructions in alloclen.
Also range check alt_exit_addr directly in the else-if condition.
Since exit_addr handling now falls through to the epilogue, two
related issues in bpf_int_jit_compile() must also be addressed:
1. Reset cgctx.alt_exit_addr before the second size-calculation pass.
Without this, a stale alt_exit_addr from the first pass causes the
second pass to emit a single jump instead of the full epilogue,
undercounting alloclen and reintroducing the overflow.
2. Recompute addrs[fp->len] at the end of each code-generation pass.
The larger pass-0 body can shrink in later passes as out-of-range
exits settle into in-range jumps; a stale addrs[fp->len] would
leave exit branches targeting past the real (shrunken) epilogue.
Because shrinkage in a later pass can move the epilogue offset, the
fixed two-pass loop is no longer sufficient: an exit that was out of
range in an earlier pass may fall in range once the epilogue offset
shrinks, shrinking the body further and overwriting the start of the
epilogue. Convert the code-generation loop to iterate until the
program size converges, bounded by CODEGEN_MAX_PASSES, and fail the
JIT if it does not converge.
Reported-by: sashiko-bot@kernel.org
Closes: https://lore.kernel.org/bpf/20260529015855.364704-2-adubey@linux.ibm.com/T/#mfcb23909d977b949727cca4f59ee56a13fd69b92
Fixes: d243b62b7bd3 ("powerpc64/bpf: Add support for bpf trampolines")
Cc: stable@vger.kernel.org
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
Signed-off-by: Abhishek Dubey <adubey@linux.ibm.com>
Signed-off-by: Saket Kumar Bhaskar <skb99@linux.ibm.com>
Link: https://lore.kernel.org/bpf/20260529015855.364704-2-adubey@linux.ibm.com/T/#mfcb23909d977b949727cca4f59ee56a13fd69b92
Tested-by: Yeswanth Krishna Tellakula <yeswanth@linux.ibm.com>
---
arch/powerpc/net/bpf_jit.h | 7 +++++++
arch/powerpc/net/bpf_jit_comp.c | 34 +++++++++++++++++++++++++--------
2 files changed, 33 insertions(+), 8 deletions(-)
diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h
index 35015d7ecb76..6d58df361648 100644
--- a/arch/powerpc/net/bpf_jit.h
+++ b/arch/powerpc/net/bpf_jit.h
@@ -14,6 +14,13 @@
#include <asm/ppc-opcode.h>
#include <linux/build_bug.h>
+/*
+ * We need at least 2 passes for proper code generation, and may need
+ * additional passes if code size changes between passes.
+ */
+#define CODEGEN_MIN_PASSES 2
+#define CODEGEN_MAX_PASSES 3
+
#ifdef CONFIG_PPC64_ELF_ABI_V1
#define FUNCTION_DESCR_SIZE 24
#else
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 8f7501954d9f..11981d2270a9 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -99,11 +99,10 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx)
int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr)
{
- if (!exit_addr || is_offset_in_branch_range(exit_addr - (ctx->idx * 4))) {
+ if (exit_addr && is_offset_in_branch_range(exit_addr - (long)(ctx->idx * 4))) {
PPC_JMP(exit_addr);
- } else if (ctx->alt_exit_addr) {
- if (WARN_ON(!is_offset_in_branch_range((long)ctx->alt_exit_addr - (ctx->idx * 4))))
- return -1;
+ } else if (ctx->alt_exit_addr && is_offset_in_branch_range(
+ (long)(ctx->alt_exit_addr) - (long)(ctx->idx * 4))) {
PPC_JMP(ctx->alt_exit_addr);
} else {
ctx->alt_exit_addr = ctx->idx * 4;
@@ -274,6 +273,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
*/
if (cgctx.seen & SEEN_TAILCALL || !is_offset_in_branch_range((long)cgctx.idx * 4)) {
cgctx.idx = 0;
+ cgctx.alt_exit_addr = 0;
if (bpf_jit_build_body(fp, NULL, NULL, &cgctx, addrs, 0, false))
goto out_err;
}
@@ -306,10 +306,13 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
code_base = (u32 *)(image + FUNCTION_DESCR_SIZE);
fcode_base = (u32 *)(fimage + FUNCTION_DESCR_SIZE);
- /* Code generation passes 1-2 */
- for (pass = 1; pass < 3; pass++) {
+ /* Code generation passes 1-2+, loop until program size converges. */
+ for (pass = 1; pass <= CODEGEN_MAX_PASSES; pass++) {
+ u32 prev_proglen = proglen;
+
/* Now build the prologue, body code & epilogue for real. */
cgctx.idx = 0;
+ cgctx.exentry_idx = 0;
cgctx.alt_exit_addr = 0;
bpf_jit_build_prologue(code_base, &cgctx);
if (bpf_jit_build_body(fp, code_base, fcode_base, &cgctx, addrs, pass,
@@ -318,11 +321,26 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
bpf_jit_binary_pack_free(fhdr, hdr);
goto out_err;
}
+ addrs[fp->len] = cgctx.idx * 4;
bpf_jit_build_epilogue(code_base, &cgctx);
+ proglen = cgctx.idx * 4;
+
if (bpf_jit_enable > 1)
pr_info("Pass %d: shrink = %d, seen = 0x%x\n", pass,
- proglen - (cgctx.idx * 4), cgctx.seen);
+ prev_proglen - proglen, cgctx.seen);
+
+ /* Check if program size has converged, but ensure minimum passes */
+ if (pass >= CODEGEN_MIN_PASSES && proglen == prev_proglen)
+ break;
+
+ if (pass == CODEGEN_MAX_PASSES && proglen != prev_proglen) {
+ pr_err("BPF JIT: Program did not converge after %d passes\n",
+ CODEGEN_MAX_PASSES);
+ bpf_arch_text_copy(&fhdr->size, &hdr->size, sizeof(hdr->size));
+ bpf_jit_binary_pack_free(fhdr, hdr);
+ goto out_err;
+ }
}
if (bpf_jit_enable > 1)
@@ -399,7 +417,7 @@ int bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, u32 *fimage, int pass
u32 *fixup;
/* Populate extable entries only in the last pass */
- if (pass != 2)
+ if (pass < CODEGEN_MIN_PASSES)
return 0;
if (!fp->aux->extable ||
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v13 4/8] powerpc/bpf: fix alignment of long branch trampoline address
2026-08-31 7:15 [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage Saket Kumar Bhaskar
` (2 preceding siblings ...)
2026-08-31 7:16 ` [PATCH v13 3/8] powerpc/bpf: fix buffer overflow in JIT for large BPF programs Saket Kumar Bhaskar
@ 2026-08-31 7:16 ` Saket Kumar Bhaskar
2026-08-31 8:12 ` bot+bpf-ci
2026-08-31 7:16 ` [PATCH v13 5/8] powerpc/bpf: Move out dummy_tramp_addr after Long branch stub Saket Kumar Bhaskar
` (3 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Saket Kumar Bhaskar @ 2026-08-31 7:16 UTC (permalink / raw)
To: bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99
From: Abhishek Dubey <adubey@linux.ibm.com>
Ensure the dummy trampoline address field present between the OOL stub
and the long branch stub is 4/8-byte aligned, for memory compatibility
when content loaded to a register.
Reported-by: Hari Bathini <hbathini@linux.ibm.com>
Fixes: d243b62b7bd3 ("powerpc64/bpf: Add support for bpf trampolines")
Cc: stable@vger.kernel.org
Signed-off-by: Abhishek Dubey <adubey@linux.ibm.com>
Signed-off-by: Saket Kumar Bhaskar <skb99@linux.ibm.com>
Tested-by: Yeswanth Krishna Tellakula <yeswanth@linux.ibm.com>
---
arch/powerpc/net/bpf_jit.h | 7 +++---
arch/powerpc/net/bpf_jit_comp.c | 38 ++++++++++++++++++++++++++-----
arch/powerpc/net/bpf_jit_comp32.c | 7 +++---
arch/powerpc/net/bpf_jit_comp64.c | 7 +++---
4 files changed, 44 insertions(+), 15 deletions(-)
diff --git a/arch/powerpc/net/bpf_jit.h b/arch/powerpc/net/bpf_jit.h
index 6d58df361648..4da8bde92e1e 100644
--- a/arch/powerpc/net/bpf_jit.h
+++ b/arch/powerpc/net/bpf_jit.h
@@ -227,10 +227,11 @@ int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context *
int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct codegen_context *ctx,
u32 *addrs, int pass, bool extra_pass);
void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx);
-void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx);
-void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx);
+void bpf_jit_build_epilogue(u32 *image, u32 *fimage, struct codegen_context *ctx);
+void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context *ctx);
void bpf_jit_realloc_regs(struct codegen_context *ctx);
-int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr);
+int bpf_jit_emit_exit_insn(u32 *image, u32 *fimage, struct codegen_context *ctx, int tmp_reg,
+ long exit_addr);
void prepare_for_fsession_fentry(u32 *image, struct codegen_context *ctx, int cookie_cnt,
int cookie_off, int retval_off);
void store_func_meta(u32 *image, struct codegen_context *ctx, u64 func_meta, int func_meta_off);
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 11981d2270a9..8ca36a933c7a 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -49,11 +49,35 @@ asm (
" .popsection ;"
);
-void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx)
+void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context *ctx)
{
int ool_stub_idx, long_branch_stub_idx;
+ int ool_stub_sz;
/*
+ * Align the mis-aligned dummy_tramp_addr field in the fimage.
+ * The alignment NOP must appear before OOL stub, to make
+ * ool_stub_idx & long_branch_stub_idx constant from end.
+ *
+ * The fimage can be non 8-byte aligned, so final alignment depends
+ * on start of fimage and the stub's instruction count offset. The
+ * OOL stub size is 4 instructions (with CONFIG_PPC_FTRACE_OUT_OF_LINE)
+ * or 3 instructions (without) before dummy_tramp_addr.
+ *
+ * Emit a NOP here if address is not SZL aligned.
+ *
+ * In pass=0 when image==NULL, conservatively account for space
+ * required to accommodate alignment NOP. In case final pass skips
+ * emitting alignment NOP, the image buffer have 4 spare bytes and
+ * jited_len signifies correct program size.
+ */
+
+ ool_stub_sz = IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) ? 16 : 12;
+ if (!image || !IS_ALIGNED((unsigned long)fimage + ctx->idx*4 + ool_stub_sz, SZL))
+ EMIT(PPC_RAW_NOP());
+
+ /*
+ * nop // optional, for alignment of dummy_tramp_addr
* Out-of-line stub:
* mflr r0
* [b|bl] tramp
@@ -70,7 +94,7 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx)
/*
* Long branch stub:
- * .long <dummy_tramp_addr>
+ * .long <dummy_tramp_addr> // 8-byte aligned
* mflr r11
* bcl 20,31,$+4
* mflr r12
@@ -81,6 +105,7 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx)
*/
if (image)
*((unsigned long *)&image[ctx->idx]) = (unsigned long)dummy_tramp;
+
ctx->idx += SZL / 4;
long_branch_stub_idx = ctx->idx;
EMIT(PPC_RAW_MFLR(_R11));
@@ -97,7 +122,8 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx)
}
}
-int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr)
+int bpf_jit_emit_exit_insn(u32 *image, u32 *fimage, struct codegen_context *ctx,
+ int tmp_reg, long exit_addr)
{
if (exit_addr && is_offset_in_branch_range(exit_addr - (long)(ctx->idx * 4))) {
PPC_JMP(exit_addr);
@@ -106,7 +132,7 @@ int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg,
PPC_JMP(ctx->alt_exit_addr);
} else {
ctx->alt_exit_addr = ctx->idx * 4;
- bpf_jit_build_epilogue(image, ctx);
+ bpf_jit_build_epilogue(image, fimage, ctx);
}
return 0;
@@ -286,7 +312,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
*/
bpf_jit_build_prologue(NULL, &cgctx);
addrs[fp->len] = cgctx.idx * 4;
- bpf_jit_build_epilogue(NULL, &cgctx);
+ bpf_jit_build_epilogue(NULL, NULL, &cgctx);
fixup_len = fp->aux->num_exentries * BPF_FIXUP_LEN * 4;
extable_len = fp->aux->num_exentries * sizeof(struct exception_table_entry);
@@ -322,7 +348,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_pr
goto out_err;
}
addrs[fp->len] = cgctx.idx * 4;
- bpf_jit_build_epilogue(code_base, &cgctx);
+ bpf_jit_build_epilogue(code_base, fcode_base, &cgctx);
proglen = cgctx.idx * 4;
diff --git a/arch/powerpc/net/bpf_jit_comp32.c b/arch/powerpc/net/bpf_jit_comp32.c
index bfdc50740da8..f5b9441cf46a 100644
--- a/arch/powerpc/net/bpf_jit_comp32.c
+++ b/arch/powerpc/net/bpf_jit_comp32.c
@@ -229,7 +229,7 @@ static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx
}
-void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
+void bpf_jit_build_epilogue(u32 *image, u32 *fimage, struct codegen_context *ctx)
{
EMIT(PPC_RAW_MR(_R3, bpf_to_ppc(BPF_REG_0)));
@@ -237,7 +237,7 @@ void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
EMIT(PPC_RAW_BLR());
- bpf_jit_build_fentry_stubs(image, ctx);
+ bpf_jit_build_fentry_stubs(image, fimage, ctx);
}
/* Relative offset needs to be calculated based on final image location */
@@ -1149,7 +1149,8 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
* we'll just fall through to the epilogue.
*/
if (i != flen - 1) {
- ret = bpf_jit_emit_exit_insn(image, ctx, _R0, exit_addr);
+ ret = bpf_jit_emit_exit_insn(image, fimage,
+ ctx, _R0, exit_addr);
if (ret)
return ret;
}
diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c
index 59302cabc466..e80312171aa7 100644
--- a/arch/powerpc/net/bpf_jit_comp64.c
+++ b/arch/powerpc/net/bpf_jit_comp64.c
@@ -398,7 +398,7 @@ static void bpf_jit_emit_common_epilogue(u32 *image, struct codegen_context *ctx
}
}
-void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
+void bpf_jit_build_epilogue(u32 *image, u32 *fimage, struct codegen_context *ctx)
{
bpf_jit_emit_common_epilogue(image, ctx);
@@ -407,7 +407,7 @@ void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx)
EMIT(PPC_RAW_BLR());
- bpf_jit_build_fentry_stubs(image, ctx);
+ bpf_jit_build_fentry_stubs(image, fimage, ctx);
}
/*
@@ -1737,7 +1737,8 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
* we'll just fall through to the epilogue.
*/
if (i != flen - 1) {
- ret = bpf_jit_emit_exit_insn(image, ctx, tmp1_reg, exit_addr);
+ ret = bpf_jit_emit_exit_insn(image, fimage, ctx,
+ tmp1_reg, exit_addr);
if (ret)
return ret;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v13 5/8] powerpc/bpf: Move out dummy_tramp_addr after Long branch stub
2026-08-31 7:15 [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage Saket Kumar Bhaskar
` (3 preceding siblings ...)
2026-08-31 7:16 ` [PATCH v13 4/8] powerpc/bpf: fix alignment of long branch trampoline address Saket Kumar Bhaskar
@ 2026-08-31 7:16 ` Saket Kumar Bhaskar
2026-08-31 7:16 ` [PATCH v13 6/8] selftests/bpf: Fix powerpc JIT disassembly failure Saket Kumar Bhaskar
` (2 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Saket Kumar Bhaskar @ 2026-08-31 7:16 UTC (permalink / raw)
To: bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99
From: Abhishek Dubey <adubey@linux.ibm.com>
Move the long branch address field to the bottom of the long
branch stub. This allows uninterrupted disassembly until the
last 4/8 bytes. The last bytes exclusion is logically necessary to
prevent disassembly failure, otherwise the actual program layout
is never altered. Hence no effect on overall program size.
Also, align dummy_tramp_addr field with 8-byte boundary.
Following is disassembler output for test program with moved down
dummy_tramp_addr field:
.....
.....
pc:68 left:44 a6 03 08 7c : mtlr 0
pc:72 left:40 bc ff ff 4b : b .-68
pc:76 left:36 a6 02 68 7d : mflr 11
pc:80 left:32 05 00 9f 42 : bcl 20, 31, .+4
pc:84 left:28 a6 02 88 7d : mflr 12
pc:88 left:24 14 00 8c e9 : ld 12, 20(12)
pc:92 left:20 a6 03 89 7d : mtctr 12
pc:96 left:16 a6 03 68 7d : mtlr 11
pc:100 left:12 20 04 80 4e : bctr
pc:104 left:8 c0 34 1d 00 :
Failure log:
Can't disasm instruction at offset 104: c0 34 1d 00 00 00 00 c0
Disassembly logic can truncate at 104, ignoring last 8 bytes.
Update the dummy_tramp_addr field offset calculation from the end
of the program to reflect its new location, for bpf_arch_text_poke()
to update the actual trampoline's address in this field.
All BPF trampoline selftests continue to pass with this patch applied.
Signed-off-by: Abhishek Dubey <adubey@linux.ibm.com>
Signed-off-by: Saket Kumar Bhaskar <skb99@linux.ibm.com>
Tested-by: Yeswanth Krishna Tellakula <yeswanth@linux.ibm.com>
---
arch/powerpc/net/bpf_jit_comp.c | 44 ++++++++++++++++++---------------
1 file changed, 24 insertions(+), 20 deletions(-)
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 8ca36a933c7a..c2717f0d9cdd 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -52,19 +52,19 @@ asm (
void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context *ctx)
{
int ool_stub_idx, long_branch_stub_idx;
- int ool_stub_sz;
+ int stub_sz;
/*
+ * The dummy_tramp_addr field is placed at bottom of Long branch stub.
* Align the mis-aligned dummy_tramp_addr field in the fimage.
* The alignment NOP must appear before OOL stub, to make
* ool_stub_idx & long_branch_stub_idx constant from end.
*
* The fimage can be non 8-byte aligned, so final alignment depends
- * on start of fimage and the stub's instruction count offset. The
- * OOL stub size is 4 instructions (with CONFIG_PPC_FTRACE_OUT_OF_LINE)
- * or 3 instructions (without) before dummy_tramp_addr.
- *
- * Emit a NOP here if address is not SZL aligned.
+ * on start of fimage and the stub's instruction count. The
+ * stubs block has 11 instructions (with CONFIG_PPC_FTRACE_OUT_OF_LINE)
+ * or 10 instructions (without) before dummy_tramp_addr field. Emit a
+ * NOP if the address of dummy_tramp_addr is non aligned.
*
* In pass=0 when image==NULL, conservatively account for space
* required to accommodate alignment NOP. In case final pass skips
@@ -72,8 +72,8 @@ void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context
* jited_len signifies correct program size.
*/
- ool_stub_sz = IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) ? 16 : 12;
- if (!image || !IS_ALIGNED((unsigned long)fimage + ctx->idx*4 + ool_stub_sz, SZL))
+ stub_sz = IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) ? 44 : 40;
+ if (!image || !IS_ALIGNED((unsigned long)fimage + ctx->idx*4 + stub_sz, SZL))
EMIT(PPC_RAW_NOP());
/*
@@ -94,28 +94,29 @@ void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context
/*
* Long branch stub:
- * .long <dummy_tramp_addr> // 8-byte aligned
* mflr r11
* bcl 20,31,$+4
- * mflr r12
- * ld r12, -8-SZL(r12)
+ * mflr r12 // lr/r12 stores pc of current(this) inst.
+ * ld r12, 20(r12) // offset(dummy_tramp_addr) from prev inst. is 20
* mtctr r12
- * mtlr r11 // needed to retain ftrace ABI
+ * mtlr r11 // needed to retain ftrace ABI
* bctr
+ * .long <dummy_tramp_addr> // SZL bytes aligned
*/
- if (image)
- *((unsigned long *)&image[ctx->idx]) = (unsigned long)dummy_tramp;
-
- ctx->idx += SZL / 4;
long_branch_stub_idx = ctx->idx;
EMIT(PPC_RAW_MFLR(_R11));
EMIT(PPC_RAW_BCL4());
EMIT(PPC_RAW_MFLR(_R12));
- EMIT(PPC_RAW_LL(_R12, _R12, -8-SZL));
+ EMIT(PPC_RAW_LL(_R12, _R12, 20));
EMIT(PPC_RAW_MTCTR(_R12));
EMIT(PPC_RAW_MTLR(_R11));
EMIT(PPC_RAW_BCTR());
+ if (image)
+ *((unsigned long *)&image[ctx->idx]) = (unsigned long)dummy_tramp;
+
+ ctx->idx += SZL / 4;
+
if (!bpf_jit_ool_stub) {
bpf_jit_ool_stub = (ctx->idx - ool_stub_idx) * 4;
bpf_jit_long_branch_stub = (ctx->idx - long_branch_stub_idx) * 4;
@@ -1309,6 +1310,7 @@ static void do_isync(void *info __maybe_unused)
* bpf_func:
* [nop|b] ool_stub
* 2. Out-of-line stub:
+ * nop // optional nop for alignment
* ool_stub:
* mflr r0
* [b|bl] <bpf_prog>/<long_branch_stub>
@@ -1316,14 +1318,14 @@ static void do_isync(void *info __maybe_unused)
* b bpf_func + 4
* 3. Long branch stub:
* long_branch_stub:
- * .long <branch_addr>/<dummy_tramp>
* mflr r11
* bcl 20,31,$+4
* mflr r12
- * ld r12, -16(r12)
+ * ld r12, 20(r12)
* mtctr r12
* mtlr r11 // needed to retain ftrace ABI
* bctr
+ * .long <branch_addr>/<dummy_tramp>
*
* dummy_tramp is used to reduce synchronization requirements.
*
@@ -1425,10 +1427,12 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
* 1. Update the address in the long branch stub:
* If new_addr is out of range, we will have to use the long branch stub, so patch new_addr
* here. Otherwise, revert to dummy_tramp, but only if we had patched old_addr here.
+ *
+ * dummy_tramp_addr moved to bottom of long branch stub.
*/
if ((new_addr && !is_offset_in_branch_range(new_addr - ip)) ||
(old_addr && !is_offset_in_branch_range(old_addr - ip)))
- ret = patch_ulong((void *)(bpf_func_end - bpf_jit_long_branch_stub - SZL),
+ ret = patch_ulong((void *)(bpf_func_end - SZL), /* SZL: dummy_tramp_addr offset */
(new_addr && !is_offset_in_branch_range(new_addr - ip)) ?
(unsigned long)new_addr : (unsigned long)dummy_tramp);
if (ret)
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v13 6/8] selftests/bpf: Fix powerpc JIT disassembly failure
2026-08-31 7:15 [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage Saket Kumar Bhaskar
` (4 preceding siblings ...)
2026-08-31 7:16 ` [PATCH v13 5/8] powerpc/bpf: Move out dummy_tramp_addr after Long branch stub Saket Kumar Bhaskar
@ 2026-08-31 7:16 ` Saket Kumar Bhaskar
2026-08-31 8:12 ` bot+bpf-ci
2026-08-31 7:16 ` [PATCH v13 7/8] selftests/bpf: Enable verifier selftest for powerpc64 Saket Kumar Bhaskar
2026-08-31 7:16 ` [PATCH v13 8/8] selftests/bpf: Add tailcall " Saket Kumar Bhaskar
7 siblings, 1 reply; 13+ messages in thread
From: Saket Kumar Bhaskar @ 2026-08-31 7:16 UTC (permalink / raw)
To: bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99
From: Abhishek Dubey <adubey@linux.ibm.com>
Ensure that the trampoline stubs JITed at the tail of the
epilogue do not expose the dummy trampoline address stored
in the last 8 bytes(64-bit) and last 4 bytes(32-bit)
to the disassembly flow. Prevent the disassembler from
ingesting this memory address, as it may occasionally decode
into a seemingly valid but incorrect instruction. Fix this
issue by truncating the last 8/4 bytes from JITed buffers
before supplying them for disassembly.
Signed-off-by: Abhishek Dubey <adubey@linux.ibm.com>
Signed-off-by: Saket Kumar Bhaskar <skb99@linux.ibm.com>
Tested-by: Yeswanth Krishna Tellakula <yeswanth@linux.ibm.com>
Reviewed-by: Hari Bathini <hbathini@linux.ibm.com>
---
.../selftests/bpf/jit_disasm_helpers.c | 25 ++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/jit_disasm_helpers.c b/tools/testing/selftests/bpf/jit_disasm_helpers.c
index 3558fe10e28c..b291d934dd1a 100644
--- a/tools/testing/selftests/bpf/jit_disasm_helpers.c
+++ b/tools/testing/selftests/bpf/jit_disasm_helpers.c
@@ -179,9 +179,11 @@ int get_jited_program_text(int fd, char *text, size_t text_sz)
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
__u32 jited_funcs, len, pc;
+ __u32 trunc_len = 0, disasm_len;
__u32 *func_lens = NULL;
FILE *text_out = NULL;
uint8_t *image = NULL;
+ char *triple = NULL;
int i, err = 0;
if (!llvm_initialized) {
@@ -225,9 +227,30 @@ int get_jited_program_text(int fd, char *text, size_t text_sz)
if (!ASSERT_OK(err, "bpf_prog_get_info_by_fd #2"))
goto out;
+ /*
+ * last 8 bytes contains dummy_trampoline address in JIT
+ * output on 64-bit and last 4 bytes on 32-bit powerpc,
+ * which can't disassemble to a valid instruction.
+ */
+ triple = LLVMGetDefaultTargetTriple();
+ if (triple) {
+ if (strstr(triple, "powerpc64") || strstr(triple, "ppc64"))
+ trunc_len = 8;
+ else if (strstr(triple, "powerpc") || strstr(triple, "ppc"))
+ trunc_len = 4;
+ LLVMDisposeMessage(triple);
+ }
+
for (pc = 0, i = 0; i < jited_funcs; ++i) {
fprintf(text_out, "func #%d:\n", i);
- disasm_one_func(text_out, image + pc, func_lens[i]);
+
+ /*
+ * Disabled JIT have zero func_lens, hence underflow
+ */
+ disasm_len = func_lens[i] > trunc_len ?
+ func_lens[i] - trunc_len : 0;
+ disasm_one_func(text_out, image + pc, disasm_len);
+
fprintf(text_out, "\n");
pc += func_lens[i];
}
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v13 7/8] selftests/bpf: Enable verifier selftest for powerpc64
2026-08-31 7:15 [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage Saket Kumar Bhaskar
` (5 preceding siblings ...)
2026-08-31 7:16 ` [PATCH v13 6/8] selftests/bpf: Fix powerpc JIT disassembly failure Saket Kumar Bhaskar
@ 2026-08-31 7:16 ` Saket Kumar Bhaskar
2026-08-31 7:16 ` [PATCH v13 8/8] selftests/bpf: Add tailcall " Saket Kumar Bhaskar
7 siblings, 0 replies; 13+ messages in thread
From: Saket Kumar Bhaskar @ 2026-08-31 7:16 UTC (permalink / raw)
To: bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99
From: Abhishek Dubey <adubey@linux.ibm.com>
Enable "__arch_powerpc64" specifier in verifier selftest
for ppc64. Power 32-bit would require separate handling.
Changes tested for 64-bit only.
Signed-off-by: Abhishek Dubey <adubey@linux.ibm.com>
Signed-off-by: Saket Kumar Bhaskar <skb99@linux.ibm.com>
Reviewed-by: Hari Bathini <hbathini@linux.ibm.com>
Tested-by: Yeswanth Krishna Tellakula <yeswanth@linux.ibm.com>
---
tools/testing/selftests/bpf/progs/bpf_misc.h | 1 +
tools/testing/selftests/bpf/test_loader.c | 5 +++++
2 files changed, 6 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
index b0c441384f20..365008d1ed40 100644
--- a/tools/testing/selftests/bpf/progs/bpf_misc.h
+++ b/tools/testing/selftests/bpf/progs/bpf_misc.h
@@ -159,6 +159,7 @@
#define __arch_riscv64 __arch("RISCV64")
#define __arch_s390x __arch("s390x")
#define __arch_loongarch __arch("LOONGARCH")
+#define __arch_powerpc64 __arch("POWERPC64")
#define __caps_unpriv(caps) __test_tag("test_caps_unpriv=" EXPAND_QUOTE(caps))
#define __load_if_JITed() __test_tag("load_mode=jited")
#define __load_if_no_JITed() __test_tag("load_mode=no_jited")
diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
index 3ce32d134e2c..01b1420097d2 100644
--- a/tools/testing/selftests/bpf/test_loader.c
+++ b/tools/testing/selftests/bpf/test_loader.c
@@ -378,6 +378,7 @@ enum arch {
ARCH_RISCV64 = 0x8,
ARCH_S390X = 0x10,
ARCH_LOONGARCH = 0x20,
+ ARCH_POWERPC64 = 0x40,
};
static int get_current_arch(void)
@@ -392,6 +393,8 @@ static int get_current_arch(void)
return ARCH_S390X;
#elif defined(__loongarch__)
return ARCH_LOONGARCH;
+#elif defined(__powerpc64__)
+ return ARCH_POWERPC64;
#endif
return ARCH_UNKNOWN;
}
@@ -585,6 +588,8 @@ static int parse_test_spec(struct test_loader *tester,
arch = ARCH_S390X;
} else if (strcmp(val, "LOONGARCH") == 0) {
arch = ARCH_LOONGARCH;
+ } else if (strcmp(val, "POWERPC64") == 0) {
+ arch = ARCH_POWERPC64;
} else {
PRINT_FAIL("bad arch spec: '%s'\n", val);
err = -EINVAL;
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v13 8/8] selftests/bpf: Add tailcall verifier selftest for powerpc64
2026-08-31 7:15 [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage Saket Kumar Bhaskar
` (6 preceding siblings ...)
2026-08-31 7:16 ` [PATCH v13 7/8] selftests/bpf: Enable verifier selftest for powerpc64 Saket Kumar Bhaskar
@ 2026-08-31 7:16 ` Saket Kumar Bhaskar
2026-08-31 8:12 ` bot+bpf-ci
7 siblings, 1 reply; 13+ messages in thread
From: Saket Kumar Bhaskar @ 2026-08-31 7:16 UTC (permalink / raw)
To: bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99
From: Abhishek Dubey <adubey@linux.ibm.com>
Verifier testcase result for tailcalls:
# ./test_progs -t verifier_tailcall
#618/1 verifier_tailcall/invalid map type for tail call:OK
#618/2 verifier_tailcall/invalid map type for tail call @unpriv:OK
#618 verifier_tailcall:OK
#619/1 verifier_tailcall_jit/main:OK
#619 verifier_tailcall_jit:OK
Summary: 2/3 PASSED, 0 SKIPPED, 0 FAILED
Signed-off-by: Abhishek Dubey <adubey@linux.ibm.com>
Signed-off-by: Saket Kumar Bhaskar <skb99@linux.ibm.com>
Tested-by: Yeswanth Krishna Tellakula <yeswanth@linux.ibm.com>
---
.../bpf/progs/verifier_tailcall_jit.c | 78 +++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c b/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
index 48fa34d2959f..9880ce4f1931 100644
--- a/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
+++ b/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
@@ -91,6 +91,84 @@ __jited(" popq %rax")
__jited(" jmp {{.*}}") /* jump to tail call tgt */
__jited("L0: leave")
__jited(" {{(retq|jmp 0x)}}") /* return or jump to rethunk */
+__arch_powerpc64
+/* program entry for main(), regular function prologue */
+__jited(" nop")
+__jited("...") /* ld 2, 16(13) absent with CONFIG_PPC_KERNEL_PCREL */
+__jited(" li 9, 0")
+__jited(" std 9, -8(1)")
+__jited(" mflr 0")
+__jited(" std 0, 16(1)")
+__jited(" stdu 1, {{.*}}(1)")
+/*
+ * load address and call sub() via count register
+ *
+ * Address materialization differs between PCREL and non-PCREL kernels.
+ * Skip the address generation sequence and verify only that the call
+ * target is loaded into CTR before branching.
+ */
+__jited("...")
+__jited("...")
+__jited("...")
+__jited("...")
+__jited("...")
+__jited("...")
+__jited(" mtctr {{.*}}")
+__jited("...")
+__jited(" bctrl")
+__jited(" mr 8, 3")
+__jited(" li 8, 0")
+__jited(" addi 1, 1, {{.*}}")
+__jited(" ld 0, 16(1)")
+__jited(" mtlr 0")
+__jited(" mr 3, 8")
+__jited(" blr")
+__jited("...")
+__jited("func #1")
+/* subprogram entry for sub() */
+__jited(" nop")
+__jited("...") /* ld 2, 16(13) absent with CONFIG_PPC_KERNEL_PCREL */
+/* tail call prologue for subprogram */
+__jited(" ld 10, 0(1)")
+__jited(" ld 9, -8(10)")
+__jited(" cmpldi 9, 33")
+__jited(" bt {{.*}}, {{.*}}")
+__jited(" addi 9, 10, -8")
+__jited(" std 9, -8(1)")
+__jited(" lis {{.*}}, {{.*}}")
+__jited(" sldi {{.*}}, {{.*}}, 32")
+__jited(" oris {{.*}}, {{.*}}, {{.*}}")
+__jited(" ori {{.*}}, {{.*}}, {{.*}}")
+__jited(" li {{.*}}, 0")
+__jited(" lwz 9, {{.*}}({{.*}})")
+__jited(" slwi {{.*}}, {{.*}}, 0")
+__jited(" cmplw {{.*}}, 9")
+__jited(" bf 0, {{.*}}")
+/* bpf_tail_call implementation */
+__jited(" ld 9, -8(1)")
+__jited(" cmpldi 9, 33")
+__jited(" bf {{.*}}, {{.*}}")
+__jited(" ld 9, 0(9)")
+__jited(" cmpldi 9, 33")
+__jited(" bt {{.*}}, {{.*}}")
+__jited(" addi 9, 9, 1")
+__jited(" mulli 10, {{.*}}, 8")
+__jited(" add 10, 10, {{.*}}")
+__jited(" ld 10, {{.*}}(10)")
+__jited(" cmpldi 10, 0")
+__jited(" bt {{.*}}, {{.*}}")
+__jited(" ld 10, {{.*}}(10)")
+__jited(" addi 10, 10, {{.*}}") /* offset depends on CONFIG_PPC_KERNEL_PCREL */
+__jited(" mtctr 10")
+__jited(" ld 10, -8(1)")
+__jited(" cmpldi 10, 33")
+__jited(" bt {{.*}}, {{.*}}")
+__jited(" addi 10, 1, -8")
+__jited(" std 9, 0(10)")
+__jited(" bctr")
+__jited(" mr 3, 8")
+__jited(" blr")
+
SEC("tc")
__naked int main(void)
{
--
2.55.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v13 6/8] selftests/bpf: Fix powerpc JIT disassembly failure
2026-08-31 7:16 ` [PATCH v13 6/8] selftests/bpf: Fix powerpc JIT disassembly failure Saket Kumar Bhaskar
@ 2026-08-31 8:12 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2026-08-31 8:12 UTC (permalink / raw)
To: skb99, bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2328 bytes --]
> selftests/bpf: Fix powerpc JIT disassembly failure
>
> Ensure that the trampoline stubs JITed at the tail of the
> epilogue do not expose the dummy trampoline address stored
> in the last 8 bytes(64-bit) and last 4 bytes(32-bit)
> to the disassembly flow. Prevent the disassembler from
> ingesting this memory address, as it may occasionally decode
> into a seemingly valid but incorrect instruction. Fix this
> issue by truncating the last 8/4 bytes from JITed buffers
> before supplying them for disassembly.
Does the changelog accurately describe when this fix takes effect?
Looking at this commit, get_jited_program_text() is only called from
run_subtest() in tools/testing/selftests/bpf/test_loader.c when
subspec->jited.cnt != 0. The __jited expectations are only recorded
when the preceding __arch_* tag matches the running architecture:
} else if ((val = str_has_pfx(s, "test_arch="))) {
...
collect_jit = get_current_arch() == arch;
Since get_current_arch() in this tree has no powerpc case, it returns
ARCH_UNKNOWN, no __arch_* tag ever matches on powerpc, jited.cnt stays
0, and get_jited_program_text() is never invoked on powerpc.
The path only becomes reachable with the later patches in the same
series, 422d7386c6a2b (which adds ARCH_POWERPC64 / __arch_powerpc64) and
b787dda9a979d (which adds tailcall verifier selftest). Would it be more
accurate to describe this as a prerequisite for that enablement rather
than as a fix for a current failure?
Also, the changelog says the address "may occasionally decode into a
seemingly valid but incorrect instruction", whereas the comment added by
this same hunk says the bytes "can't disassemble to a valid
instruction", and the companion patch 0cd8bd7da278d quotes the concrete
symptom:
Can't disasm instruction at offset 104: c0 34 1d 00 00 00 00 c0
A hard "Can't disasm" abort (what disasm_insn() reports via PRINT_FAIL)
is a different problem from a mis-decode into a plausible-looking
instruction. Could the commit message be more precise about which
failure mode actually occurs?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33368376983
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v13 4/8] powerpc/bpf: fix alignment of long branch trampoline address
2026-08-31 7:16 ` [PATCH v13 4/8] powerpc/bpf: fix alignment of long branch trampoline address Saket Kumar Bhaskar
@ 2026-08-31 8:12 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2026-08-31 8:12 UTC (permalink / raw)
To: skb99, bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3760 bytes --]
> diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
> index 11981d2270a9d..8ca36a933c7ae 100644
> --- a/arch/powerpc/net/bpf_jit_comp.c
> +++ b/arch/powerpc/net/bpf_jit_comp.c
[ ... ]
> @@ -49,11 +49,35 @@ asm (
> " .popsection ;"
> );
>
> -void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx)
> +void bpf_jit_build_fentry_stubs(u32 *image, u32 *fimage, struct codegen_context *ctx)
> {
> int ool_stub_idx, long_branch_stub_idx;
> + int ool_stub_sz;
>
> /*
> + * Align the mis-aligned dummy_tramp_addr field in the fimage.
> + * The alignment NOP must appear before OOL stub, to make
> + * ool_stub_idx & long_branch_stub_idx constant from end.
> + *
> + * The fimage can be non 8-byte aligned, so final alignment depends
> + * on start of fimage and the stub's instruction count offset. The
> + * OOL stub size is 4 instructions (with CONFIG_PPC_FTRACE_OUT_OF_LINE)
> + * or 3 instructions (without) before dummy_tramp_addr.
> + *
> + * Emit a NOP here if address is not SZL aligned.
> + *
> + * In pass=0 when image==NULL, conservatively account for space
> + * required to accommodate alignment NOP. In case final pass skips
> + * emitting alignment NOP, the image buffer have 4 spare bytes and
> + * jited_len signifies correct program size.
> + */
> +
> + ool_stub_sz = IS_ENABLED(CONFIG_PPC_FTRACE_OUT_OF_LINE) ? 16 : 12;
> + if (!image || !IS_ALIGNED((unsigned long)fimage + ctx->idx*4 + ool_stub_sz, SZL))
> + EMIT(PPC_RAW_NOP());
Can the conditional alignment NOP here mask program length convergence?
The preceding commit (5175364d6174 "powerpc/bpf: fix buffer overflow in
JIT for large BPF programs") added a convergence check:
if (pass >= CODEGEN_MIN_PASSES && proglen == prev_proglen)
break;
to bpf_int_jit_compile(). But the alignment decision above forces the
stub block to end at a fixed residue mod 8: the field address (fimage +
ctx->idx*4 + ool_stub_sz) is aligned to SZL, and exactly SZL/4 + 7
instructions follow it. So fimage + proglen is congruent to a constant
mod 8 every pass, meaning proglen can only change in multiples of 8.
When the body shrinks by an odd multiple of 4 bytes between passes, the
NOP can absorb it and keep proglen unchanged. The body can shrink when
an exit goes from out-of-range (emitting a full epilogue) to in-range
(emitting a single branch):
arch/powerpc/net/bpf_jit_comp.c:bpf_jit_emit_exit_insn() {
if (exit_addr && is_offset_in_branch_range(...)) {
PPC_JMP(exit_addr); // 1 instruction
} else {
...
bpf_jit_build_epilogue(image, fimage, ctx); // N instructions
}
}
The shrink is (N-1)*4 bytes. When N-1 is odd, this is 4 mod 8. The NOP
appearance or disappearance compensates, making proglen identical across
passes even though addrs[] shifted.
Because forward branches use addrs[] from the previous pass (addrs[j]
for target j > current i is computed in the prior pass), those branches
would land (N-1)*4 bytes past the intended target.
The comment at arch/powerpc/net/bpf_jit.h:132-138 documents that the
PowerPC JIT avoids pass-to-pass size changes by padding the short branch
case with a NOP, specifically to prevent this scenario. Does reintroducing
an address-dependent, pass-varying size risk incorrect branches?
(Note: commit 0cd8bd7da278 later in this series reworks the stub layout
and is described as a layout improvement rather than a fix for this commit,
which may provide additional context.)
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33368376983
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v13 8/8] selftests/bpf: Add tailcall verifier selftest for powerpc64
2026-08-31 7:16 ` [PATCH v13 8/8] selftests/bpf: Add tailcall " Saket Kumar Bhaskar
@ 2026-08-31 8:12 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2026-08-31 8:12 UTC (permalink / raw)
To: skb99, bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 6221 bytes --]
> diff --git a/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c b/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
> index 48fa34d2959f4..9880ce4f1931a 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_tailcall_jit.c
> @@ -91,6 +91,84 @@ __jited(" popq %rax")
> __jited(" jmp {{.*}}") /* jump to tail call tgt */
> __jited("L0: leave")
> __jited(" {{(retq|jmp 0x)}}") /* return or jump to rethunk */
> +__arch_powerpc64
> +/* program entry for main(), regular function prologue */
> +__jited(" nop")
Will this test work on CONFIG_PPC64_ELF_ABI_V1 builds? On big-endian ppc64
kernels, arch/powerpc/net/bpf_jit_comp.c prepends a 24-byte function
descriptor to the JIT image:
#ifdef CONFIG_PPC64_ELF_ABI_V1
/* Function descriptor nastiness: Address + TOC */
((u64 *)image)[0] = (u64)fcode_base;
((u64 *)image)[1] = local_paca->kernel_toc;
#endif
fp->bpf_func = (void *)fimage;
fp->jited_len = cgctx.idx * 4 + FUNCTION_DESCR_SIZE;
and bpf_prog_get_info_by_fd() copies jited_len bytes starting at
prog->bpf_func. The disassembler then receives those 24 descriptor bytes as
if they were instructions. The low half of a typical kernel_toc address
such as 0xc000000001b70000 decodes from primary opcode 0, which is illegal,
causing disasm_insn() to call PRINT_FAIL("Can't disasm instruction...") and
fail the test.
The companion commit 9e3a62e5e00a6 truncates the trailing 8 bytes for this
reason, but nothing strips the leading descriptor. Should the test be
restricted to ELFv2, or should jit_disasm_helpers.c skip
FUNCTION_DESCR_SIZE at the head of each function on ppc64 ELFv1?
> +__jited("...") /* ld 2, 16(13) absent with CONFIG_PPC_KERNEL_PCREL */
Is this comment accurate? The TOC load is guarded by both conditions:
#ifndef CONFIG_PPC_KERNEL_PCREL
if (IS_ENABLED(CONFIG_PPC64_ELF_ABI_V2))
EMIT(PPC_RAW_LD(_R2, _R13, offsetof(struct paca_struct, kernel_toc)));
#endif
so it is also absent on CONFIG_PPC64_ELF_ABI_V1 builds.
Also, is the displacement always 16? The offsetof(struct paca_struct,
kernel_toc) is 16 only when CONFIG_PPC_PSERIES=y, because the leading
'struct lppaca *lppaca_ptr' member is inside '#ifdef CONFIG_PPC_PSERIES'.
Without PSERIES the offset is 8, giving 'ld 2, 8(13)'.
> +__jited(" li 9, 0")
> +__jited(" std 9, -8(1)")
> +__jited(" mflr 0")
> +__jited(" std 0, 16(1)")
> +__jited(" stdu 1, {{.*}}(1)")
> +/*
> + * load address and call sub() via count register
> + *
> + * Address materialization differs between PCREL and non-PCREL kernels.
> + * Skip the address generation sequence and verify only that the call
> + * target is loaded into CTR before branching.
> + */
> +__jited("...")
> +__jited("...")
> +__jited("...")
> +__jited("...")
> +__jited("...")
> +__jited("...")
Does the framework need six consecutive '...' entries? In
tools/testing/selftests/bpf/test_loader.c, the push_disasm_msg()
function handles '...' by setting on_next_line = false and returning
immediately without pushing a pattern:
if (strcmp(regex_str, "...") == 0) {
*on_next_line = false;
return 0;
}
So '...' pushes no pattern at all; it only clears the on_next_line flag
for the following real pattern, which then matches anywhere later in the
log. Consecutive '...' entries are idempotent. These six lines behave
identically to a single '...', and five of them are dead. The comment
describes skipping 6 instructions, but the framework doesn't implement
per-line skips this way.
> +__jited(" mtctr {{.*}}")
[ ... ]
> +__jited("...")
> +__jited("func #1")
> +/* subprogram entry for sub() */
> +__jited(" nop")
> +__jited("...") /* ld 2, 16(13) absent with CONFIG_PPC_KERNEL_PCREL */
This comment is repeated verbatim from the main() prologue and has the same
issues: the TOC load is also absent on ELFv1, and the displacement varies
with CONFIG_PPC_PSERIES.
> +/* tail call prologue for subprogram */
> +__jited(" ld 10, 0(1)")
> +__jited(" ld 9, -8(10)")
> +__jited(" cmpldi 9, 33")
> +__jited(" bt {{.*}}, {{.*}}")
> +__jited(" addi 9, 10, -8")
> +__jited(" std 9, -8(1)")
> +__jited(" lis {{.*}}, {{.*}}")
> +__jited(" sldi {{.*}}, {{.*}}, 32")
> +__jited(" oris {{.*}}, {{.*}}, {{.*}}")
> +__jited(" ori {{.*}}, {{.*}}, {{.*}}")
Will this lis/sldi/oris/ori sequence always appear in exactly this form?
PPC_LI64() in arch/powerpc/net/bpf_jit.h emits between 2 and 5
instructions, conditionally, based on the runtime value:
EMIT(PPC_RAW_LIS(d, (uintptr_t)(i) >> 48));
if ((uintptr_t)(i) & 0x0000ffff00000000ULL)
EMIT(PPC_RAW_ORI(d, d, ((uintptr_t)(i) >> 32) & 0xffff));
EMIT(PPC_RAW_SLDI(d, d, 32));
if ((uintptr_t)(i) & 0x00000000ffff0000ULL)
EMIT(PPC_RAW_ORIS(d, d, ((uintptr_t)(i) >> 16) & 0xffff));
if ((uintptr_t)(i) & 0x000000000000ffffULL)
EMIT(PPC_RAW_ORI(d, d, (uintptr_t)(i) & 0xffff));
Each of these four patterns follows a non-'...' pattern, so test_loader.c
sets on_next_line=true and requires strict line adjacency. The verifier
rewrites BPF_LD_IMM64 to the kernel address of the kvzalloc()'d 'struct
bpf_array', which is a linear-map address 0xc000000000000000 + physaddr.
When that object lands above the 4 GiB boundary (routine with >4 GiB RAM),
PPC_LI64() emits an extra 'ori' between 'lis' and 'sldi', and the 'sldi'
pattern then matches one line late, causing validate_msgs() to report
WRONG LINE.
Symmetrically, a 64 KiB-aligned map address drops the trailing 'ori', and a
map whose bits 16..31 are zero drops the 'oris', both producing
EXPECTED/no-match failures. The test already recognizes this problem for
the call-target address (the comment about PCREL vs non-PCREL address
materialization plus '...' wildcards). Should the map-address
materialization use a single '__jited("...")' instead?
> +__jited(" li {{.*}}, 0")
> +__jited(" lwz 9, {{.*}}({{.*}})")
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33368376983
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v13 3/8] powerpc/bpf: fix buffer overflow in JIT for large BPF programs
2026-08-31 7:16 ` [PATCH v13 3/8] powerpc/bpf: fix buffer overflow in JIT for large BPF programs Saket Kumar Bhaskar
@ 2026-08-31 8:24 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2026-08-31 8:24 UTC (permalink / raw)
To: skb99, bpf, linuxppc-dev
Cc: hbathini, maddy, ast, andrii, daniel, shuah, linux-kselftest,
stable, venkat88, yeswanth, skb99, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 5230 bytes --]
> Fixes: d243b62b7bd3 ("powerpc64/bpf: Add support for bpf trampolines")
Does the Fixes: tag name the correct commit?
With Cc: stable@vger.kernel.org this fix will only reach v6.13+ and will
leave every stable tree from v5.18 to v6.12 carrying the same JIT buffer
overflow.
The faulty logic predates d243b62b7bd3. Looking at the parent of that commit:
git show d243b62b7bd3^:arch/powerpc/net/bpf_jit_comp.c
already contains the exact code being fixed:
if (!exit_addr || is_offset_in_branch_range(exit_addr - (ctx->idx * 4))) {
PPC_JMP(exit_addr);
} else if (ctx->alt_exit_addr) {
if (WARN_ON(!is_offset_in_branch_range((long)ctx->alt_exit_addr - (ctx->idx * 4))))
return -1;
PPC_JMP(ctx->alt_exit_addr);
} else {
ctx->alt_exit_addr = ctx->idx * 4;
bpf_jit_build_epilogue(image, ctx);
}
The real introducing commit is 0ffdbce6f4a89bb7c0002904d6438ec83cf05ce7
("powerpc/bpf: Handle large branch ranges with BPF_EXIT"), merged in v5.18-rc1
(git describe --contains: v5.18-rc1~124^2~42). It created
bpf_jit_emit_exit_insn() with the '!exit_addr' early-out and replaced the
previous unconditional one-instruction emission:
- if (i != flen - 1)
- PPC_JMP(exit_addr);
+ if (i != flen - 1) {
+ ret = bpf_jit_emit_exit_insn(image, ctx, b2p[TMP_REG_1], exit_addr);
Before 0ffdbce6f4a8 a non-final BPF_EXIT always cost exactly one instruction in
every pass, so the sizing pass could not under-count and no overflow was
possible. After it, the sizing pass emits PPC_JMP(0) (1 instruction) while
later passes may emit a full inline epilogue, which is precisely the
under-count described in this commit message.
The commit d243b62b7bd3 only enlarged the epilogue (it added
bpf_jit_build_fentry_stubs()), making an already-existing overflow bigger.
Citing the ppc64-trampoline commit also wrongly implies PPC32 is unaffected.
The commit 0ffdbce6f4a8 made the identical change to
arch/powerpc/net/bpf_jit_comp32.c, so 32-bit powerpc has the same bug over the
same range.
A tag survey confirms the range: 'alt_exit_addr' has 0 occurrences in
arch/powerpc/net/ at v5.16 and v5.17, and 5 occurrences at v5.18, v5.19, v6.0,
v6.1 and v6.6.
Should the tag be:
Fixes: 0ffdbce6f4a8 ("powerpc/bpf: Handle large branch ranges with BPF_EXIT")
> diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
> index 8f7501954d9f0..11981d2270a9d 100644
> --- a/arch/powerpc/net/bpf_jit_comp.c
> +++ b/arch/powerpc/net/bpf_jit_comp.c
> @@ -99,11 +99,10 @@ void bpf_jit_build_fentry_stubs(u32 *image, struct codegen_context *ctx)
>
> int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr)
> {
> - if (!exit_addr || is_offset_in_branch_range(exit_addr - (ctx->idx * 4))) {
> + if (exit_addr && is_offset_in_branch_range(exit_addr - (long)(ctx->idx * 4))) {
> PPC_JMP(exit_addr);
> - } else if (ctx->alt_exit_addr) {
> - if (WARN_ON(!is_offset_in_branch_range((long)ctx->alt_exit_addr - (ctx->idx * 4))))
> - return -1;
> + } else if (ctx->alt_exit_addr && is_offset_in_branch_range(
> + (long)(ctx->alt_exit_addr) - (long)(ctx->idx * 4))) {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> PPC_JMP(ctx->alt_exit_addr);
With alt_exit_addr defined as unsigned int, does the expression
(long)(ctx->alt_exit_addr) - (long)(ctx->idx * 4) produce a different result
than (long)ctx->alt_exit_addr - (ctx->idx * 4) due to how integer promotion
works?
In v7 review, Hari Bathini pointed out that the cast placement may affect
integer promotion semantics and requested restoring the earlier syntax with the
cast only on alt_exit_addr:
https://lore.kernel.org/bpf/038a115b-e2a5-4ecf-82b1-3689535e986b@linux.ibm.com
While the current code has passed testing, the concern about integer promotion
semantics was never explicitly addressed.
[ ... ]
> @@ -399,7 +417,7 @@ int bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, u32 *fimage, int pass
> u32 *fixup;
>
> /* Populate extable entries only in the last pass */
> - if (pass != 2)
> + if (pass < CODEGEN_MIN_PASSES)
> return 0;
Does the comment still match the code?
With the old 'if (pass != 2)' and a fixed two-pass loop, pass 2 was the last
pass, so the comment was accurate. With the new loop and 'pass <
CODEGEN_MIN_PASSES', both pass 2 and pass 3 can write extable entries, so
entries are populated in every pass from 2 onwards, not only in the last one.
Also, CODEGEN_MIN_PASSES is being reused here with a different meaning. In
arch/powerpc/net/bpf_jit.h it documents the minimum number of code-generation
passes, but here it is used as 'the first pass in which extable entries are
valid'. The two are logically independent - bumping CODEGEN_MIN_PASSES to 3 to
force more settling iterations would silently stop extable entries being
written in pass 2.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/33368376983
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-08-31 8:25 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 7:15 [PATCH v13 0/8] powerpc/bpf: address missing verifier selftest coverage Saket Kumar Bhaskar
2026-08-31 7:15 ` [PATCH v13 1/8] powerpc64/bpf: fix compare instruction emitted for tailcall Saket Kumar Bhaskar
2026-08-31 7:15 ` [PATCH v13 2/8] powerpc64/bpf: fix percpu private stack leak on JIT failure Saket Kumar Bhaskar
2026-08-31 7:16 ` [PATCH v13 3/8] powerpc/bpf: fix buffer overflow in JIT for large BPF programs Saket Kumar Bhaskar
2026-08-31 8:24 ` bot+bpf-ci
2026-08-31 7:16 ` [PATCH v13 4/8] powerpc/bpf: fix alignment of long branch trampoline address Saket Kumar Bhaskar
2026-08-31 8:12 ` bot+bpf-ci
2026-08-31 7:16 ` [PATCH v13 5/8] powerpc/bpf: Move out dummy_tramp_addr after Long branch stub Saket Kumar Bhaskar
2026-08-31 7:16 ` [PATCH v13 6/8] selftests/bpf: Fix powerpc JIT disassembly failure Saket Kumar Bhaskar
2026-08-31 8:12 ` bot+bpf-ci
2026-08-31 7:16 ` [PATCH v13 7/8] selftests/bpf: Enable verifier selftest for powerpc64 Saket Kumar Bhaskar
2026-08-31 7:16 ` [PATCH v13 8/8] selftests/bpf: Add tailcall " Saket Kumar Bhaskar
2026-08-31 8:12 ` bot+bpf-ci
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox