[PATCH v10 3/8] selftest/bpf: Fixing powerpc JIT disassembly failure

Subsystems: bpf [general] (safe dynamic programs and tools), bpf [selftests] (test runners & infrastructure), kernel selftest framework, the rest

COLD60d REVIEWED: 1 (0M)

1 review trailer.

4 messages, 3 authors, 2026-08-06 · open the first message on its own page

[PATCH v10 3/8] selftest/bpf: Fixing powerpc JIT disassembly failure

From: Saket Kumar Bhaskar <hidden>
Date: 2026-08-05 06:07:48

From: Abhishek Dubey <redacted>

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.

Fixes: b991fc520700 ("selftests/bpf: utility function to get program disassembly after jit")
Signed-off-by: Abhishek Dubey <redacted>
Signed-off-by: Saket Kumar Bhaskar <redacted>
Tested-by: Yeswanth Krishna Tellakula <redacted>
---
 .../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..c4aa1d69d3bb 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.54.0

Re: [PATCH v10 3/8] selftest/bpf: Fixing powerpc JIT disassembly failure

From: bot+bpf-ci@kernel.org
Date: 2026-08-05 07:37:34

selftest/bpf: Fixing 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.

Fixes: b991fc520700 ("selftests/bpf: utility function to get program disassembly after jit")
The Fixes: tag names b991fc520700 ("selftests/bpf: utility function to get
program disassembly after jit", 2024-08-21), which is the commit that
introduced get_jited_program_text() itself. That helper was not wrong when
written: at that time no powerpc JIT emitted a raw data word inside a
program, and the helper is documented (jit_disasm_helpers.c:13) as serving
small assembly test programs.

The undisassemblable trailing bytes come from d243b62b7bd3 ("powerpc64/bpf:
Add support for bpf trampolines", 2024-10-31) - the same commit both
sibling patches in this series cite - and only become observable once
powerpc jited selftests are enabled, which happens later in this series in
d3841599dc7ca ("selftest/bpf: Enable verifier selftest for powerpc64").

More importantly, the fix as written is only correct on top of its
immediate parent. Truncating the last trunc_len bytes assumes
dummy_tramp_addr is the final thing in the JITed image. That layout is
established by the immediate parent c6ba4edfe6e46 ("powerpc/bpf: Move out
dummy_tramp_addr after Long branch stub"), which moved the data word from
the top of the long branch stub to its bottom:

arch/powerpc/net/bpf_jit_comp.c:bpf_jit_build_fentry_stubs() {
    ...
    long_branch_stub_idx = ctx->idx;
    EMIT(PPC_RAW_MFLR(_R11));
    ...
    EMIT(PPC_RAW_BCTR());

    if (image)
            *((unsigned long *)&image[ctx->idx]) = (unsigned long)dummy_tramp;

    ctx->idx += SZL / 4;
}

On any tree that has b991fc520700 but not c6ba4edfe6e46, dummy_tramp_addr
sits before the seven long-branch instructions, so trimming the tail
removes two real instructions (bctr and mtlr r11 on 64-bit) while leaving
the data word in the middle of the disassembled range - the disassembly
failure this patch targets is not fixed, and expected __jited() output is
silently altered.

A backport driven by the stated Fixes: tag alone would therefore land a
change that does not work. Should the tag point at the commit that
introduced the byte pattern (d243b62b7bd3), or should the dependency on
c6ba4edfe6e46 be stated explicitly in the changelog?
quoted hunk
diff --git a/tools/testing/selftests/bpf/jit_disasm_helpers.c b/tools/testing/selftests/bpf/jit_disasm_helpers.c
index 3558fe10e28cf..c4aa1d69d3bbf 100644
--- a/tools/testing/selftests/bpf/jit_disasm_helpers.c
+++ b/tools/testing/selftests/bpf/jit_disasm_helpers.c
[ ... ]
quoted hunk
@@ -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.
+	 */
This isn't a bug, but the comment says the trailing bytes "can't
disassemble to a valid instruction", while the changelog for the same
change says the opposite - "it may occasionally decode into a seemingly
valid but incorrect instruction" - which is the case that actually
motivates unconditional truncation rather than tolerating a disasm
failure.

The comment also names the field "dummy_trampoline", whereas the arch code
calls the symbol "dummy_tramp" and the field "dummy_tramp_addr"
(arch/powerpc/net/bpf_jit_comp.c:118), so grepping from the comment does
not lead to the code.

Separately, the second added comment, "Disabled JIT have zero func_lens,
hence underflow", is a sentence fragment that leaves the reader to infer
that the ternary exists to avoid a __u32 wrap when func_lens[i] is 0.

Could this comment use the "dummy_tramp_addr" name from the arch code and
describe the failure the same way the changelog does, so the two do not
read as contradictory?
quoted hunk
+	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);
[ ... ]
quoted hunk
+
 		fprintf(text_out, "\n");
 		pc += func_lens[i];
 	}
This isn't a bug, but two blank lines are added inside the loop body that
are unrelated to the fix: one immediately after the "for (...) {" opening
brace, and one between the disasm_one_func() call and the following
fprintf(). Neither separates logical blocks - the loop body is six lines -
and no other loop in this file opens with a blank line (compare the two
"while (pc < len)" loops in disasm_one_func(), which start directly with
their first statement).

Are the two new blank lines inside this loop intentional, or could the
hunk stay limited to the truncation change?
selftest/bpf: Fixing powerpc JIT disassembly failure
This isn't a bug, but the subject uses the prefix "selftest/bpf:"
(singular) where the tree's overwhelmingly dominant prefix for this
directory is "selftests/bpf:" - 1272 commits versus 10 in recent history.
The subject also uses the gerund "Fixing" rather than the imperative mood
the submitting-patches guidance asks for ("Fix ..." / "Skip ...").

Would "selftests/bpf: Skip trailing trampoline address bytes when
disassembling" fit the usual prefix and imperative style for this
directory better?


---
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/30981212698

Re: [PATCH v10 3/8] selftest/bpf: Fixing powerpc JIT disassembly failure

From: Hari Bathini <hbathini@linux.ibm.com>
Date: 2026-08-06 17:20:00


On 05/08/26 11:36 am, Saket Kumar Bhaskar wrote:
From: Abhishek Dubey <redacted>

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.
Fixes: b991fc520700 ("selftests/bpf: utility function to get program disassembly after jit")
The fixes tag on this patch doesn't really help. Please drop it.
quoted hunk
Signed-off-by: Abhishek Dubey <redacted>
Signed-off-by: Saket Kumar Bhaskar <redacted>
Tested-by: Yeswanth Krishna Tellakula <redacted>
---
  .../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..c4aa1d69d3bb 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];
  	}
- Hari

Re: [PATCH v10 3/8] selftest/bpf: Fixing powerpc JIT disassembly failure

From: Hari Bathini <hbathini@linux.ibm.com>
Date: 2026-08-06 17:21:14


On 06/08/26 10:49 pm, Hari Bathini wrote:

On 05/08/26 11:36 am, Saket Kumar Bhaskar wrote:
quoted
From: Abhishek Dubey <redacted>

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.
quoted
Fixes: b991fc520700 ("selftests/bpf: utility function to get program disassembly after jit")
The fixes tag on this patch doesn't really help. Please drop it.
With that..

Reviewed-by: Hari Bathini <hbathini@linux.ibm.com>
quoted
Signed-off-by: Abhishek Dubey <redacted>
Signed-off-by: Saket Kumar Bhaskar <redacted>
Tested-by: Yeswanth Krishna Tellakula <redacted>
---
  .../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..c4aa1d69d3bb 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];
      }
- Hari

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help