BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next v9 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args()
@ 2026-04-29 17:19 Yazhou Tang
  2026-04-29 17:19 ` [PATCH bpf-next v9 1/3] bpf: Fix out-of-bounds " Yazhou Tang
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Yazhou Tang @ 2026-04-29 17:19 UTC (permalink / raw)
  To: bpf, ast, eddyz87, emil, puranjay, xukuohai
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye

From: Yazhou Tang <tangyazhou518@outlook.com>

This patchset addresses a silent truncation bug in the BPF verifier that
occurs when a bpf-to-bpf call involves a massive relative jump offset.
Additionally, it fixes a pre-existing out-of-bounds (OOB) read issue
in the interpreter fallback path.

Because the BPF instruction set utilizes a 32-bit imm field for bpf-to-bpf
calls, implicitly downcasting it to the 16-bit insn->off in bpf_patch_call_args()
causes incorrect call targets or subprog ID resolution for large BPF programs.
While fixing this by swapping the imm and off fields, it was discovered that
the original code also had a load-time OOB read vulnerability when the stack
depth exceeds MAX_BPF_STACK during JIT fallback.

Patch 1/3 fixes the pre-existing OOB read in bpf_patch_call_args(). It
changes the function to return an int and explicitly rejects the JIT
fallback if the stack depth exceeds MAX_BPF_STACK, preventing a potential
stack buffer overflow.

Patch 2/3 fixes the s16 truncation bug. It keeps the original imm field
unchanged and uses the off field to store the interpreter function index.
It also adjusts the JMP_CALL_ARGS case in ___bpf_prog_run() and the dumper
code accordingly, while safely removing a previous workaround in the
selftests disasm helpers.

Patch 3/3 introduces a selftest for this fix.

---

Change log:

v9:
1. Modify the selftest in patch 3/3: use __clobber_all in inline asm.
   (Sashiko AI reviewer)

v8: https://lore.kernel.org/bpf/20260429105608.92741-1-tangyazhou@zju.edu.cn/
1. Update cfg_partition_funcs() in bpftool to use insn->imm for call target
   calculation. (Sashiko AI reviewer)
2. Modify the selftest in patch 3/3: add a large padding before the call
   instruction, preventing the kernel panic on kernel without the fix.
   (Sashiko AI reviewer)
3. Modify the selftest in patch 3/3: make it more clear.

v7: https://lore.kernel.org/bpf/20260421144504.823756-1-tangyazhou@zju.edu.cn/
1. Rebase the patchset to the bpf-next tree to resolve the apply conflict.
   (Alexei)
2. Add Patch 1/3 to properly fix a pre-existing OOB read in bpf_patch_call_args().
   (Sashiko AI reviewer)

v6: https://lore.kernel.org/bpf/20260412170334.716778-1-tangyazhou@zju.edu.cn/
1. Use a different but clearer approach to resolve this issue: keeping
   the original imm field unchanged and using the off field to store the
   interpreter function index. (Kuohai)
2. Update the related dumper code and remove a previous workaround in the
   selftests disasm helpers, which is no longer needed after this fix.

v5: https://lore.kernel.org/bpf/20260326090133.221957-1-tangyazhou@zju.edu.cn/
1. Some minor changes in commit messages. (AI Reviewer)

v4: https://lore.kernel.org/bpf/20260326063329.10031-1-tangyazhou@zju.edu.cn/
1. Remove some redundant commit messages of patch 2/3. (Emil)
2. Change the number of instructions in padding_subprog() from 200,000
   to 32,765, which is the minimum number of instructions required to
   trigger the verifier failure. (Emil)

v3: https://lore.kernel.org/bpf/20260323122254.98540-1-tangyazhou@zju.edu.cn/
1. Resend to fix a typo in v2 and add "Fixes" tag. The rest of the changes
   are identical to v2.

v2 (incorrect): https://lore.kernel.org/bpf/20260323081748.106603-1-tangyazhou@zju.edu.cn/
1. Move the s16 boundary check from fixup_call_args() to bpf_patch_call_args(),
   and change the return type of bpf_patch_call_args() to int. (Emil)
2. Add Patch 3/3 to fix the incorrect subprog ID in dumped bpf_pseudo_call
   instructions, which is caused by the same truncation issue. (Puranjay)
3. Refine the new selftest for clarity and add detailed comments explaining
   the test design. (Emil)

v1: https://lore.kernel.org/bpf/20260316190220.113417-1-tangyazhou@zju.edu.cn/

Yazhou Tang (3):
  bpf: Fix out-of-bounds read in bpf_patch_call_args()
  bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  selftests/bpf: Add test for large offset bpf-to-bpf call

 include/linux/bpf.h                           |  2 +-
 include/linux/filter.h                        |  3 -
 kernel/bpf/core.c                             | 17 +++--
 kernel/bpf/fixups.c                           | 12 ++--
 tools/bpf/bpftool/cfg.c                       |  2 +-
 tools/bpf/bpftool/xlated_dumper.c             | 10 +--
 tools/testing/selftests/bpf/disasm_helpers.c  | 18 -----
 .../selftests/bpf/prog_tests/verifier.c       |  2 +
 .../bpf/progs/verifier_call_large_imm.c       | 66 +++++++++++++++++++
 9 files changed, 94 insertions(+), 38 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/verifier_call_large_imm.c

-- 
2.54.0


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH bpf-next v9 1/3] bpf: Fix out-of-bounds read in bpf_patch_call_args()
  2026-04-29 17:19 [PATCH bpf-next v9 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
@ 2026-04-29 17:19 ` Yazhou Tang
  2026-04-30  3:29   ` Xu Kuohai
  2026-04-29 17:19 ` [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang
  2026-04-29 17:19 ` [PATCH bpf-next v9 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang
  2 siblings, 1 reply; 15+ messages in thread
From: Yazhou Tang @ 2026-04-29 17:19 UTC (permalink / raw)
  To: bpf, ast, eddyz87, emil, puranjay, xukuohai
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye

From: Yazhou Tang <tangyazhou518@outlook.com>

The interpreters_args array only accommodates stack depths up to
MAX_BPF_STACK (512 bytes). However, do_misc_fixups() may allow a larger
stack depth if JIT is requested.

If JIT compilation later fails and falls back to the interpreter, the
verifier invokes bpf_patch_call_args() with this oversized stack depth.
This causes a load-time out-of-bounds (OOB) read when calculating the
interpreter function pointer index.

Fix this by changing bpf_patch_call_args() to return an int and explicitly
rejecting the JIT fallback (returning -EINVAL) if the stack depth exceeds
MAX_BPF_STACK.

Fixes: 1ea47e01ad6e ("bpf: add support for bpf_call to interpreter")
Co-developed-by: Tianci Cao <ziye@zju.edu.cn>
Signed-off-by: Tianci Cao <ziye@zju.edu.cn>
Co-developed-by: Shenghao Yuan <shenghaoyuan0928@163.com>
Signed-off-by: Shenghao Yuan <shenghaoyuan0928@163.com>
Signed-off-by: Yazhou Tang <tangyazhou518@outlook.com>
---
 include/linux/bpf.h | 2 +-
 kernel/bpf/core.c   | 6 +++++-
 kernel/bpf/fixups.c | 6 +++++-
 3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 715b6df9c403..eb6b816bc8b5 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2922,7 +2922,7 @@ int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,
 int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size);
 
 #ifndef CONFIG_BPF_JIT_ALWAYS_ON
-void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
+int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
 #endif
 
 struct btf *bpf_get_btf_vmlinux(void);
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index ae10b9ca018d..fcc881909969 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -2394,13 +2394,17 @@ EVAL4(PROG_NAME_LIST, 416, 448, 480, 512)
 #undef PROG_NAME_LIST
 
 #ifdef CONFIG_BPF_SYSCALL
-void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
+int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
 {
 	stack_depth = max_t(u32, stack_depth, 1);
+	/* Prevent out-of-bounds read to interpreters_args */
+	if (stack_depth > MAX_BPF_STACK)
+		return -EINVAL;
 	insn->off = (s16) insn->imm;
 	insn->imm = interpreters_args[(round_up(stack_depth, 32) / 32) - 1] -
 		__bpf_call_base_args;
 	insn->code = BPF_JMP | BPF_CALL_ARGS;
+	return 0;
 }
 #endif
 #endif
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index fba9e8c00878..300e4e251931 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -1416,7 +1416,11 @@ int bpf_fixup_call_args(struct bpf_verifier_env *env)
 		depth = get_callee_stack_depth(env, insn, i);
 		if (depth < 0)
 			return depth;
-		bpf_patch_call_args(insn, depth);
+		err = bpf_patch_call_args(insn, depth);
+		if (err) {
+			verbose(env, "interpreter stack depth %d is too large\n", depth);
+			return err;
+		}
 	}
 	err = 0;
 #endif
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-04-29 17:19 [PATCH bpf-next v9 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
  2026-04-29 17:19 ` [PATCH bpf-next v9 1/3] bpf: Fix out-of-bounds " Yazhou Tang
@ 2026-04-29 17:19 ` Yazhou Tang
  2026-04-29 21:10   ` sashiko-bot
  2026-04-30  3:29   ` Xu Kuohai
  2026-04-29 17:19 ` [PATCH bpf-next v9 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang
  2 siblings, 2 replies; 15+ messages in thread
From: Yazhou Tang @ 2026-04-29 17:19 UTC (permalink / raw)
  To: bpf, ast, eddyz87, emil, puranjay, xukuohai
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye

From: Yazhou Tang <tangyazhou518@outlook.com>

Currently, the BPF instruction set allows bpf-to-bpf calls (or internal
calls, pseudo calls) to use a 32-bit imm field to represent the relative
jump offset.

However, when JIT is disabled or falls back to the interpreter, the
verifier invokes bpf_patch_call_args() to rewrite the call instruction.
In this function, the 32-bit imm is downcast to s16 and stored in the off
field.

    void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
    {
        stack_depth = max_t(u32, stack_depth, 1);
        insn->off = (s16) insn->imm;
        insn->imm = interpreters_args[(round_up(stack_depth, 32) / 32) - 1] -
            __bpf_call_base_args;
        insn->code = BPF_JMP | BPF_CALL_ARGS;
    }

If the original imm exceeds the s16 range (i.e., jump offset > 32KB),
this downcast silently truncates the offset, resulting in an incorrect
call target.

Fix this by:
1. In bpf_patch_call_args(), keeping the imm field unchanged and using the
   off field to store the index of the interpreter function.
2. In ___bpf_prog_run() for the JMP_CALL_ARGS case, retrieving the
   interpreter function pointer from the interpreters_args array using the
   off field as the index, and passing the original imm to calculate the
   last argument of the interpreter function.

After these changes, the truncation issue is resolved, and __bpf_call_base_args
is also no longer needed and can be removed, which makes the code cleaner.

Performance: In ___bpf_prog_run() for the JMP_CALL_ARGS case, changing the
retrieval of the interpreter function pointer from pointer addition to
direct array indexing improves performance. The possible reason is that the
latter has better instruction-level parallelism. See the v5 discussion [1]
for more details.

[1] https://lore.kernel.org/bpf/f120c3c4-6999-414a-b514-518bb64b4758@zju.edu.cn/

The related dumper code is also updated to adapt to the verifier changes.
Specifically, the usages of insn->imm and insn->off are swapped:
1. When JIT is enabled, use insn->imm as the call offset, and insn->off
   as the subprog id.
2. When JIT is disabled, print insn->imm as the call offset.
3. The cfg_partition_funcs() function in bpftool is also updated to use
   insn->imm to calculate the call target, preventing error during
   "bpftool prog dump xlated visual".

Consequently, the workaround introduced in tools/testing/selftests/bpf/disasm_helpers.c
by commit 203e6aba7692 ("selftests/bpf: print correct offset for pseudo calls in disasm_insn()")
is no longer needed. Since the verifier now correctly exposes the offset
in the imm field, we can safely remove the custom print_call_cb() and
allow the selftests to rely on the native BPF disassembler.

Fixes: 1ea47e01ad6e ("bpf: add support for bpf_call to interpreter")
Fixes: 7105e828c087 ("bpf: allow for correlation of maps and helpers in dump")
Suggested-by: Xu Kuohai <xukuohai@huaweicloud.com>
Suggested-by: Puranjay Mohan <puranjay@kernel.org>
Co-developed-by: Tianci Cao <ziye@zju.edu.cn>
Signed-off-by: Tianci Cao <ziye@zju.edu.cn>
Co-developed-by: Shenghao Yuan <shenghaoyuan0928@163.com>
Signed-off-by: Shenghao Yuan <shenghaoyuan0928@163.com>
Signed-off-by: Yazhou Tang <tangyazhou518@outlook.com>
---
 include/linux/filter.h                       |  3 ---
 kernel/bpf/core.c                            | 11 ++++++-----
 kernel/bpf/fixups.c                          |  6 +++---
 tools/bpf/bpftool/cfg.c                      |  2 +-
 tools/bpf/bpftool/xlated_dumper.c            | 10 +++++-----
 tools/testing/selftests/bpf/disasm_helpers.c | 18 ------------------
 6 files changed, 15 insertions(+), 35 deletions(-)

diff --git a/include/linux/filter.h b/include/linux/filter.h
index b77d0b06db6e..c4d25d78291b 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -1152,9 +1152,6 @@ bool sk_filter_charge(struct sock *sk, struct sk_filter *fp);
 void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp);
 
 u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
-#define __bpf_call_base_args \
-	((u64 (*)(u64, u64, u64, u64, u64, const struct bpf_insn *)) \
-	 (void *)__bpf_call_base)
 
 struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog);
 void bpf_jit_compile(struct bpf_prog *prog);
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index fcc881909969..8f7db5f0c7e5 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1771,6 +1771,9 @@ static u32 abs_s32(s32 x)
 	return x >= 0 ? (u32)x : -(u32)x;
 }
 
+static u64 (*interpreters_args[])(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5,
+				  const struct bpf_insn *insn);
+
 /**
  *	___bpf_prog_run - run eBPF program on a given context
  *	@regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers
@@ -2077,10 +2080,10 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
 		CONT;
 
 	JMP_CALL_ARGS:
-		BPF_R0 = (__bpf_call_base_args + insn->imm)(BPF_R1, BPF_R2,
+		BPF_R0 = (interpreters_args[insn->off])(BPF_R1, BPF_R2,
 							    BPF_R3, BPF_R4,
 							    BPF_R5,
-							    insn + insn->off + 1);
+							    insn + insn->imm + 1);
 		CONT;
 
 	JMP_TAIL_CALL: {
@@ -2400,9 +2403,7 @@ int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
 	/* Prevent out-of-bounds read to interpreters_args */
 	if (stack_depth > MAX_BPF_STACK)
 		return -EINVAL;
-	insn->off = (s16) insn->imm;
-	insn->imm = interpreters_args[(round_up(stack_depth, 32) / 32) - 1] -
-		__bpf_call_base_args;
+	insn->off = (round_up(stack_depth, 32) / 32) - 1;
 	insn->code = BPF_JMP | BPF_CALL_ARGS;
 	return 0;
 }
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index 300e4e251931..8947ef74f6a8 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -1250,9 +1250,9 @@ static int jit_subprogs(struct bpf_verifier_env *env)
 		}
 		if (!bpf_pseudo_call(insn))
 			continue;
-		insn->off = env->insn_aux_data[i].call_imm;
-		subprog = bpf_find_subprog(env, i + insn->off + 1);
-		insn->imm = subprog;
+		insn->imm = env->insn_aux_data[i].call_imm;
+		subprog = bpf_find_subprog(env, i + insn->imm + 1);
+		insn->off = subprog;
 	}
 
 	prog->jited = 1;
diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
index e3785f9a697d..df43a0e0023f 100644
--- a/tools/bpf/bpftool/cfg.c
+++ b/tools/bpf/bpftool/cfg.c
@@ -142,7 +142,7 @@ static bool cfg_partition_funcs(struct cfg *cfg, struct bpf_insn *cur,
 			continue;
 		if (cur->src_reg != BPF_PSEUDO_CALL)
 			continue;
-		func = cfg_append_func(cfg, cur + cur->off + 1);
+		func = cfg_append_func(cfg, cur + cur->imm + 1);
 		if (!func)
 			return true;
 	}
diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
index 5e7cb8b36fef..6ccda6787245 100644
--- a/tools/bpf/bpftool/xlated_dumper.c
+++ b/tools/bpf/bpftool/xlated_dumper.c
@@ -150,13 +150,13 @@ static const char *print_call_pcrel(struct dump_data *dd,
 	if (!dd->nr_jited_ksyms)
 		/* Do not show address for interpreted programs */
 		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
-			"%+d", insn->off);
+			"%+d", insn->imm);
 	else if (sym)
 		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
-			 "%+d#%s", insn->off, sym->name);
+			 "%+d#%s", insn->imm, sym->name);
 	else
 		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
-			 "%+d#0x%lx", insn->off, address);
+			 "%+d#0x%lx", insn->imm, address);
 	return dd->scratch_buff;
 }
 
@@ -181,8 +181,8 @@ static const char *print_call(void *private_data,
 	struct kernel_sym *sym;
 
 	if (insn->src_reg == BPF_PSEUDO_CALL &&
-	    (__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms)
-		address = dd->jited_ksyms[insn->imm];
+	    (__u32) insn->off < dd->nr_jited_ksyms && dd->jited_ksyms)
+		address = dd->jited_ksyms[insn->off];
 
 	sym = kernel_syms_search(dd, address);
 	if (insn->src_reg == BPF_PSEUDO_CALL)
diff --git a/tools/testing/selftests/bpf/disasm_helpers.c b/tools/testing/selftests/bpf/disasm_helpers.c
index f529f1c8c171..96b1f2ffe438 100644
--- a/tools/testing/selftests/bpf/disasm_helpers.c
+++ b/tools/testing/selftests/bpf/disasm_helpers.c
@@ -4,7 +4,6 @@
 #include "disasm.h"
 
 struct print_insn_context {
-	char scratch[16];
 	char *buf;
 	size_t sz;
 };
@@ -19,22 +18,6 @@ static void print_insn_cb(void *private_data, const char *fmt, ...)
 	va_end(args);
 }
 
-static const char *print_call_cb(void *private_data, const struct bpf_insn *insn)
-{
-	struct print_insn_context *ctx = private_data;
-
-	/* For pseudo calls verifier.c:jit_subprogs() hides original
-	 * imm to insn->off and changes insn->imm to be an index of
-	 * the subprog instead.
-	 */
-	if (insn->src_reg == BPF_PSEUDO_CALL) {
-		snprintf(ctx->scratch, sizeof(ctx->scratch), "%+d", insn->off);
-		return ctx->scratch;
-	}
-
-	return NULL;
-}
-
 struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz)
 {
 	struct print_insn_context ctx = {
@@ -43,7 +26,6 @@ struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz)
 	};
 	struct bpf_insn_cbs cbs = {
 		.cb_print	= print_insn_cb,
-		.cb_call	= print_call_cb,
 		.private_data	= &ctx,
 	};
 	char *tmp, *pfx_end, *sfx_start;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH bpf-next v9 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call
  2026-04-29 17:19 [PATCH bpf-next v9 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
  2026-04-29 17:19 ` [PATCH bpf-next v9 1/3] bpf: Fix out-of-bounds " Yazhou Tang
  2026-04-29 17:19 ` [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang
@ 2026-04-29 17:19 ` Yazhou Tang
  2026-04-30  3:30   ` Xu Kuohai
  2 siblings, 1 reply; 15+ messages in thread
From: Yazhou Tang @ 2026-04-29 17:19 UTC (permalink / raw)
  To: bpf, ast, eddyz87, emil, puranjay, xukuohai
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye

From: Yazhou Tang <tangyazhou518@outlook.com>

Add a selftest to verify the verifier and JIT behavior when handling
bpf-to-bpf calls with relative jump offsets exceeding the s16 boundary.

The test utilizes an inline assembly block with ".rept 32765" to generate
a massive dummy subprogram. By placing this padding between the main
program and the target subprogram, it forces the verifier to process a
bpf-to-bpf call where the imm field exceeds the s16 range.

- When JIT is enabled, it asserts that the program is successfully loaded
  and executes correctly to return the expected value. Since patch 1/2
  does not change the JIT behavior, the test passes whether the fix is
  applied or not.
- When JIT is disabled, it also asserts that the program is successfully
  loaded and executes correctly to return the expected value 3.
  - Before the fix, the verifier rewrites the call instruction with a
    truncated offset (here 32768 -> -32768) and lets it pass. When the
    program is executed, the call instruction will go to a wrong target
    (in the landing pad) instead of the intended subprogram, then return -1
    and fail.
  - After the fix, the verifier correctly handles the large offset and
    allows it to pass. The program then executes correctly to return the
    expected value 3.

Co-developed-by: Tianci Cao <ziye@zju.edu.cn>
Signed-off-by: Tianci Cao <ziye@zju.edu.cn>
Co-developed-by: Shenghao Yuan <shenghaoyuan0928@163.com>
Signed-off-by: Shenghao Yuan <shenghaoyuan0928@163.com>
Signed-off-by: Yazhou Tang <tangyazhou518@outlook.com>
---
 .../selftests/bpf/prog_tests/verifier.c       |  2 +
 .../bpf/progs/verifier_call_large_imm.c       | 66 +++++++++++++++++++
 2 files changed, 68 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/verifier_call_large_imm.c

diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index a96b25ebff23..b27f4d4e2bd4 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -22,6 +22,7 @@
 #include "verifier_bswap.skel.h"
 #include "verifier_btf_ctx_access.skel.h"
 #include "verifier_btf_unreliable_prog.skel.h"
+#include "verifier_call_large_imm.skel.h"
 #include "verifier_cfg.skel.h"
 #include "verifier_cgroup_inv_retcode.skel.h"
 #include "verifier_cgroup_skb.skel.h"
@@ -170,6 +171,7 @@ void test_verifier_bpf_trap(void)             { RUN(verifier_bpf_trap); }
 void test_verifier_bswap(void)                { RUN(verifier_bswap); }
 void test_verifier_btf_ctx_access(void)       { RUN(verifier_btf_ctx_access); }
 void test_verifier_btf_unreliable_prog(void)  { RUN(verifier_btf_unreliable_prog); }
+void test_verifier_call_large_imm(void)       { RUN(verifier_call_large_imm); }
 void test_verifier_cfg(void)                  { RUN(verifier_cfg); }
 void test_verifier_cgroup_inv_retcode(void)   { RUN(verifier_cgroup_inv_retcode); }
 void test_verifier_cgroup_skb(void)           { RUN(verifier_cgroup_skb); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_call_large_imm.c b/tools/testing/selftests/bpf/progs/verifier_call_large_imm.c
new file mode 100644
index 000000000000..1e2c8addc582
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_call_large_imm.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+int call_happened = 0;
+
+/*
+ * 32765 is the exact minimum number of padding instructions needed to
+ * trigger the verifier failure, because:
+ * 1. Counting the wrapper instructions around the padding block (one
+ *    "r0=0" and two "exit" instructions), the actual jump distance
+ *    evaluates to N + 3.
+ * 2. To overflow the s16 max bound (32767), we need N + 3 > 32767.
+ * Thus, N = 32765 is the exact minimum padding size required.
+ */
+static __attribute__((noinline)) void padding_subprog(void)
+{
+	asm volatile (
+	"r0 = 0;"
+	".rept 32765;"
+	"r0 += 0;"
+	".endr;"
+	::: __clobber_all);
+}
+
+static __attribute__((noinline)) int target_subprog(void)
+{
+	/* Use volatile variable here to prevent optimization. */
+	volatile int magic_ret = 3;
+	return magic_ret;
+}
+
+SEC("syscall")
+__success __retval(3)
+int call_large_imm_test(void *ctx)
+{
+	/*
+	 * Landing pad to handle call error on kernel without the fix,
+	 * preventing kernel panic.
+	 */
+	asm volatile (
+	"r0 = 0;"
+	".rept 32768;"
+	"r0 += 0;"
+	".endr;"
+	::: __clobber_all);
+
+	/*
+	 * The call_happened variable is 1 only when the call insn wrongly
+	 * go back to the landing pad above.
+	 */
+	if (call_happened == 1) {
+		/* Use volatile variable here to prevent optimization. */
+		volatile int flag = -1;
+		return flag;
+	}
+
+	call_happened = 1;
+
+	padding_subprog();
+
+	return target_subprog();
+}
+
+char LICENSE[] SEC("license") = "GPL";
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-04-29 17:19 ` [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang
@ 2026-04-29 21:10   ` sashiko-bot
  2026-04-30 14:47     ` Yazhou Tang
  2026-04-30  3:29   ` Xu Kuohai
  1 sibling, 1 reply; 15+ messages in thread
From: sashiko-bot @ 2026-04-29 21:10 UTC (permalink / raw)
  To: Yazhou Tang; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Unprivileged users will see broken control flow graphs and `call pc+0` in BPF-to-BPF pseudo calls.
--

commit c7ae76817147356f188b1975260698d1d9dcd44a
Author: Yazhou Tang <tangyazhou518@outlook.com>

bpf: Fix s16 truncation for large bpf-to-bpf call offsets

This commit fixes s16 truncation for large BPF-to-BPF call offsets by keeping
the relative offset in the 32-bit imm field instead of downcasting it to the
16-bit off field. It updates the JIT, interpreter, and related dumper code to
swap the usages of imm and off for BPF pseudo calls.

> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
> index 300e4e251931..8947ef74f6a8 100644
> --- a/kernel/bpf/fixups.c
> +++ b/kernel/bpf/fixups.c
> @@ -1250,9 +1250,9 @@ static int jit_subprogs(struct bpf_verifier_env *env)
>  		}
>  		if (!bpf_pseudo_call(insn))
>  			continue;
> -		insn->off = env->insn_aux_data[i].call_imm;
> -		subprog = bpf_find_subprog(env, i + insn->off + 1);
> -		insn->imm = subprog;
> +		insn->imm = env->insn_aux_data[i].call_imm;
> +		subprog = bpf_find_subprog(env, i + insn->imm + 1);
> +		insn->off = subprog;
>  	}

Because this patch swaps the usage of insn->imm and insn->off for BPF pseudo
calls, will this cause a regression for unprivileged users dumping programs?

In kernel/bpf/syscall.c:bpf_insn_prepare_dump(), the kernel zeroes insn->imm
for all BPF_CALL instructions when the user lacks privileges:

    if (code == (BPF_JMP | BPF_CALL)) {
        insns[i].imm = 0;
        ...

Previously, this only hid the subprogram index. Now that insn->imm holds the
actual relative offset, zeroing it means tools like bpftool will receive an
offset of 0.

Could this regression cause bpftool prog dump xlated to output call pc+0 and
break the control flow graph generation for unprivileged users?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260429171904.107244-1-tangyazhou@zju.edu.cn?part=2

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 1/3] bpf: Fix out-of-bounds read in bpf_patch_call_args()
  2026-04-29 17:19 ` [PATCH bpf-next v9 1/3] bpf: Fix out-of-bounds " Yazhou Tang
@ 2026-04-30  3:29   ` Xu Kuohai
  2026-04-30 14:50     ` Yazhou Tang
  0 siblings, 1 reply; 15+ messages in thread
From: Xu Kuohai @ 2026-04-30  3:29 UTC (permalink / raw)
  To: Yazhou Tang, bpf, ast, eddyz87, emil, puranjay
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye

On 4/30/2026 1:19 AM, Yazhou Tang wrote:
> From: Yazhou Tang <tangyazhou518@outlook.com>
> 
> The interpreters_args array only accommodates stack depths up to
> MAX_BPF_STACK (512 bytes). However, do_misc_fixups() may allow a larger
> stack depth if JIT is requested.
> 
> If JIT compilation later fails and falls back to the interpreter, the
> verifier invokes bpf_patch_call_args() with this oversized stack depth.
> This causes a load-time out-of-bounds (OOB) read when calculating the
> interpreter function pointer index.
> 
> Fix this by changing bpf_patch_call_args() to return an int and explicitly
> rejecting the JIT fallback (returning -EINVAL) if the stack depth exceeds
> MAX_BPF_STACK.
> 
> Fixes: 1ea47e01ad6e ("bpf: add support for bpf_call to interpreter")
> Co-developed-by: Tianci Cao <ziye@zju.edu.cn>
> Signed-off-by: Tianci Cao <ziye@zju.edu.cn>
> Co-developed-by: Shenghao Yuan <shenghaoyuan0928@163.com>
> Signed-off-by: Shenghao Yuan <shenghaoyuan0928@163.com>
> Signed-off-by: Yazhou Tang <tangyazhou518@outlook.com>
> ---
>   include/linux/bpf.h | 2 +-
>   kernel/bpf/core.c   | 6 +++++-
>   kernel/bpf/fixups.c | 6 +++++-
>   3 files changed, 11 insertions(+), 3 deletions(-)
> 
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 715b6df9c403..eb6b816bc8b5 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -2922,7 +2922,7 @@ int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,
>   int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size);
>   
>   #ifndef CONFIG_BPF_JIT_ALWAYS_ON
> -void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
> +int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
>   #endif
>   
>   struct btf *bpf_get_btf_vmlinux(void);
> diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
> index ae10b9ca018d..fcc881909969 100644
> --- a/kernel/bpf/core.c
> +++ b/kernel/bpf/core.c
> @@ -2394,13 +2394,17 @@ EVAL4(PROG_NAME_LIST, 416, 448, 480, 512)
>   #undef PROG_NAME_LIST
>   
>   #ifdef CONFIG_BPF_SYSCALL
> -void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
> +int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
>   {
>   	stack_depth = max_t(u32, stack_depth, 1);
> +	/* Prevent out-of-bounds read to interpreters_args */
> +	if (stack_depth > MAX_BPF_STACK)
> +		return -EINVAL;
>   	insn->off = (s16) insn->imm;
>   	insn->imm = interpreters_args[(round_up(stack_depth, 32) / 32) - 1] -
>   		__bpf_call_base_args;
>   	insn->code = BPF_JMP | BPF_CALL_ARGS;
> +	return 0;
>   }
>   #endif
>   #endif
> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
> index fba9e8c00878..300e4e251931 100644
> --- a/kernel/bpf/fixups.c
> +++ b/kernel/bpf/fixups.c
> @@ -1416,7 +1416,11 @@ int bpf_fixup_call_args(struct bpf_verifier_env *env)
>   		depth = get_callee_stack_depth(env, insn, i);
>   		if (depth < 0)
>   			return depth;
> -		bpf_patch_call_args(insn, depth);
> +		err = bpf_patch_call_args(insn, depth);
> +		if (err) {
> +			verbose(env, "interpreter stack depth %d is too large\n", depth);

Nit: the log is misleading, change it to "stack depth %d exceeds interpreter
stack depth limit"?

> +			return err;
> +		}
>   	}
>   	err = 0;
>   #endif

Aside from the the log wording, it looks good to me.

Acked-by: Xu Kuohai <xukuohai@huawei.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-04-29 17:19 ` [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang
  2026-04-29 21:10   ` sashiko-bot
@ 2026-04-30  3:29   ` Xu Kuohai
  2026-04-30 15:00     ` Yazhou Tang
  1 sibling, 1 reply; 15+ messages in thread
From: Xu Kuohai @ 2026-04-30  3:29 UTC (permalink / raw)
  To: Yazhou Tang, bpf, ast, eddyz87, emil, puranjay
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye, Quentin Monnet

On 4/30/2026 1:19 AM, Yazhou Tang wrote:
> From: Yazhou Tang <tangyazhou518@outlook.com>
> 
> Currently, the BPF instruction set allows bpf-to-bpf calls (or internal
> calls, pseudo calls) to use a 32-bit imm field to represent the relative
> jump offset.
> 
> However, when JIT is disabled or falls back to the interpreter, the
> verifier invokes bpf_patch_call_args() to rewrite the call instruction.
> In this function, the 32-bit imm is downcast to s16 and stored in the off
> field.
> 
>      void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
>      {
>          stack_depth = max_t(u32, stack_depth, 1);
>          insn->off = (s16) insn->imm;
>          insn->imm = interpreters_args[(round_up(stack_depth, 32) / 32) - 1] -
>              __bpf_call_base_args;
>          insn->code = BPF_JMP | BPF_CALL_ARGS;
>      }
> 
> If the original imm exceeds the s16 range (i.e., jump offset > 32KB),
> this downcast silently truncates the offset, resulting in an incorrect
> call target.
> 
> Fix this by:
> 1. In bpf_patch_call_args(), keeping the imm field unchanged and using the
>     off field to store the index of the interpreter function.
> 2. In ___bpf_prog_run() for the JMP_CALL_ARGS case, retrieving the
>     interpreter function pointer from the interpreters_args array using the
>     off field as the index, and passing the original imm to calculate the
>     last argument of the interpreter function.
> 
> After these changes, the truncation issue is resolved, and __bpf_call_base_args
> is also no longer needed and can be removed, which makes the code cleaner.
> 
> Performance: In ___bpf_prog_run() for the JMP_CALL_ARGS case, changing the
> retrieval of the interpreter function pointer from pointer addition to
> direct array indexing improves performance. The possible reason is that the
> latter has better instruction-level parallelism. See the v5 discussion [1]
> for more details.
> 
> [1] https://lore.kernel.org/bpf/f120c3c4-6999-414a-b514-518bb64b4758@zju.edu.cn/
> 
> The related dumper code is also updated to adapt to the verifier changes.
> Specifically, the usages of insn->imm and insn->off are swapped:
> 1. When JIT is enabled, use insn->imm as the call offset, and insn->off
>     as the subprog id.
> 2. When JIT is disabled, print insn->imm as the call offset.
> 3. The cfg_partition_funcs() function in bpftool is also updated to use
>     insn->imm to calculate the call target, preventing error during
>     "bpftool prog dump xlated visual".
> 
> Consequently, the workaround introduced in tools/testing/selftests/bpf/disasm_helpers.c
> by commit 203e6aba7692 ("selftests/bpf: print correct offset for pseudo calls in disasm_insn()")
> is no longer needed. Since the verifier now correctly exposes the offset
> in the imm field, we can safely remove the custom print_call_cb() and
> allow the selftests to rely on the native BPF disassembler.
> 
> Fixes: 1ea47e01ad6e ("bpf: add support for bpf_call to interpreter")
> Fixes: 7105e828c087 ("bpf: allow for correlation of maps and helpers in dump")
> Suggested-by: Xu Kuohai <xukuohai@huaweicloud.com>
> Suggested-by: Puranjay Mohan <puranjay@kernel.org>
> Co-developed-by: Tianci Cao <ziye@zju.edu.cn>
> Signed-off-by: Tianci Cao <ziye@zju.edu.cn>
> Co-developed-by: Shenghao Yuan <shenghaoyuan0928@163.com>
> Signed-off-by: Shenghao Yuan <shenghaoyuan0928@163.com>
> Signed-off-by: Yazhou Tang <tangyazhou518@outlook.com>
> ---
>   include/linux/filter.h                       |  3 ---
>   kernel/bpf/core.c                            | 11 ++++++-----
>   kernel/bpf/fixups.c                          |  6 +++---
>   tools/bpf/bpftool/cfg.c                      |  2 +-
>   tools/bpf/bpftool/xlated_dumper.c            | 10 +++++-----
>   tools/testing/selftests/bpf/disasm_helpers.c | 18 ------------------
>   6 files changed, 15 insertions(+), 35 deletions(-)
> 
> diff --git a/include/linux/filter.h b/include/linux/filter.h
> index b77d0b06db6e..c4d25d78291b 100644
> --- a/include/linux/filter.h
> +++ b/include/linux/filter.h
> @@ -1152,9 +1152,6 @@ bool sk_filter_charge(struct sock *sk, struct sk_filter *fp);
>   void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp);
>   
>   u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
> -#define __bpf_call_base_args \
> -	((u64 (*)(u64, u64, u64, u64, u64, const struct bpf_insn *)) \
> -	 (void *)__bpf_call_base)
>   
>   struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog);
>   void bpf_jit_compile(struct bpf_prog *prog);
> diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
> index fcc881909969..8f7db5f0c7e5 100644
> --- a/kernel/bpf/core.c
> +++ b/kernel/bpf/core.c
> @@ -1771,6 +1771,9 @@ static u32 abs_s32(s32 x)
>   	return x >= 0 ? (u32)x : -(u32)x;
>   }
>   
> +static u64 (*interpreters_args[])(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5,
> +				  const struct bpf_insn *insn);
> +
>   /**
>    *	___bpf_prog_run - run eBPF program on a given context
>    *	@regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers
> @@ -2077,10 +2080,10 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
>   		CONT;
>   
>   	JMP_CALL_ARGS:
> -		BPF_R0 = (__bpf_call_base_args + insn->imm)(BPF_R1, BPF_R2,
> +		BPF_R0 = (interpreters_args[insn->off])(BPF_R1, BPF_R2,
>   							    BPF_R3, BPF_R4,
>   							    BPF_R5,
> -							    insn + insn->off + 1);
> +							    insn + insn->imm + 1);
>   		CONT;
>   
>   	JMP_TAIL_CALL: {
> @@ -2400,9 +2403,7 @@ int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
>   	/* Prevent out-of-bounds read to interpreters_args */
>   	if (stack_depth > MAX_BPF_STACK)
>   		return -EINVAL;
> -	insn->off = (s16) insn->imm;
> -	insn->imm = interpreters_args[(round_up(stack_depth, 32) / 32) - 1] -
> -		__bpf_call_base_args;
> +	insn->off = (round_up(stack_depth, 32) / 32) - 1;
>   	insn->code = BPF_JMP | BPF_CALL_ARGS;
>   	return 0;
>   }
> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
> index 300e4e251931..8947ef74f6a8 100644
> --- a/kernel/bpf/fixups.c
> +++ b/kernel/bpf/fixups.c
> @@ -1250,9 +1250,9 @@ static int jit_subprogs(struct bpf_verifier_env *env)
>   		}
>   		if (!bpf_pseudo_call(insn))
>   			continue;
> -		insn->off = env->insn_aux_data[i].call_imm;
> -		subprog = bpf_find_subprog(env, i + insn->off + 1);
> -		insn->imm = subprog;
> +		insn->imm = env->insn_aux_data[i].call_imm;
> +		subprog = bpf_find_subprog(env, i + insn->imm + 1);
> +		insn->off = subprog;
>   	}
>   
>   	prog->jited = 1;
> diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
> index e3785f9a697d..df43a0e0023f 100644
> --- a/tools/bpf/bpftool/cfg.c
> +++ b/tools/bpf/bpftool/cfg.c
> @@ -142,7 +142,7 @@ static bool cfg_partition_funcs(struct cfg *cfg, struct bpf_insn *cur,
>   			continue;
>   		if (cur->src_reg != BPF_PSEUDO_CALL)
>   			continue;
> -		func = cfg_append_func(cfg, cur + cur->off + 1);
> +		func = cfg_append_func(cfg, cur + cur->imm + 1);

Do we need to matain backward compatibility with the old bpftool here?
I suspect not, but I'm not entirely sure.

>   		if (!func)
>   			return true;
>   	}
> diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
> index 5e7cb8b36fef..6ccda6787245 100644
> --- a/tools/bpf/bpftool/xlated_dumper.c
> +++ b/tools/bpf/bpftool/xlated_dumper.c
> @@ -150,13 +150,13 @@ static const char *print_call_pcrel(struct dump_data *dd,
>   	if (!dd->nr_jited_ksyms)
>   		/* Do not show address for interpreted programs */
>   		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
> -			"%+d", insn->off);
> +			"%+d", insn->imm);
>   	else if (sym)
>   		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
> -			 "%+d#%s", insn->off, sym->name);
> +			 "%+d#%s", insn->imm, sym->name);
>   	else
>   		snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
> -			 "%+d#0x%lx", insn->off, address);
> +			 "%+d#0x%lx", insn->imm, address);
>   	return dd->scratch_buff;
>   }
>   
> @@ -181,8 +181,8 @@ static const char *print_call(void *private_data,
>   	struct kernel_sym *sym;
>   
>   	if (insn->src_reg == BPF_PSEUDO_CALL &&
> -	    (__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms)
> -		address = dd->jited_ksyms[insn->imm];
> +	    (__u32) insn->off < dd->nr_jited_ksyms && dd->jited_ksyms)
> +		address = dd->jited_ksyms[insn->off];
>   
>   	sym = kernel_syms_search(dd, address);
>   	if (insn->src_reg == BPF_PSEUDO_CALL)
> diff --git a/tools/testing/selftests/bpf/disasm_helpers.c b/tools/testing/selftests/bpf/disasm_helpers.c
> index f529f1c8c171..96b1f2ffe438 100644
> --- a/tools/testing/selftests/bpf/disasm_helpers.c
> +++ b/tools/testing/selftests/bpf/disasm_helpers.c
> @@ -4,7 +4,6 @@
>   #include "disasm.h"
>   
>   struct print_insn_context {
> -	char scratch[16];
>   	char *buf;
>   	size_t sz;
>   };
> @@ -19,22 +18,6 @@ static void print_insn_cb(void *private_data, const char *fmt, ...)
>   	va_end(args);
>   }
>   
> -static const char *print_call_cb(void *private_data, const struct bpf_insn *insn)
> -{
> -	struct print_insn_context *ctx = private_data;
> -
> -	/* For pseudo calls verifier.c:jit_subprogs() hides original
> -	 * imm to insn->off and changes insn->imm to be an index of
> -	 * the subprog instead.
> -	 */
> -	if (insn->src_reg == BPF_PSEUDO_CALL) {
> -		snprintf(ctx->scratch, sizeof(ctx->scratch), "%+d", insn->off);
> -		return ctx->scratch;
> -	}
> -
> -	return NULL;
> -}
> -
>   struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz)
>   {
>   	struct print_insn_context ctx = {
> @@ -43,7 +26,6 @@ struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz)
>   	};
>   	struct bpf_insn_cbs cbs = {
>   		.cb_print	= print_insn_cb,
> -		.cb_call	= print_call_cb,
>   		.private_data	= &ctx,
>   	};
>   	char *tmp, *pfx_end, *sfx_start;

Aside from the potential bpftool compatibility issue, it looks good to me.

Acked-by: Xu Kuohai <xukuohai@huawei.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call
  2026-04-29 17:19 ` [PATCH bpf-next v9 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang
@ 2026-04-30  3:30   ` Xu Kuohai
  0 siblings, 0 replies; 15+ messages in thread
From: Xu Kuohai @ 2026-04-30  3:30 UTC (permalink / raw)
  To: Yazhou Tang, bpf, ast, eddyz87, emil, puranjay
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye

On 4/30/2026 1:19 AM, Yazhou Tang wrote:
> From: Yazhou Tang <tangyazhou518@outlook.com>
> 
> Add a selftest to verify the verifier and JIT behavior when handling
> bpf-to-bpf calls with relative jump offsets exceeding the s16 boundary.
> 
> The test utilizes an inline assembly block with ".rept 32765" to generate
> a massive dummy subprogram. By placing this padding between the main
> program and the target subprogram, it forces the verifier to process a
> bpf-to-bpf call where the imm field exceeds the s16 range.
> 
> - When JIT is enabled, it asserts that the program is successfully loaded
>    and executes correctly to return the expected value. Since patch 1/2
>    does not change the JIT behavior, the test passes whether the fix is
>    applied or not.
> - When JIT is disabled, it also asserts that the program is successfully
>    loaded and executes correctly to return the expected value 3.
>    - Before the fix, the verifier rewrites the call instruction with a
>      truncated offset (here 32768 -> -32768) and lets it pass. When the
>      program is executed, the call instruction will go to a wrong target
>      (in the landing pad) instead of the intended subprogram, then return -1
>      and fail.
>    - After the fix, the verifier correctly handles the large offset and
>      allows it to pass. The program then executes correctly to return the
>      expected value 3.
> 
> Co-developed-by: Tianci Cao <ziye@zju.edu.cn>
> Signed-off-by: Tianci Cao <ziye@zju.edu.cn>
> Co-developed-by: Shenghao Yuan <shenghaoyuan0928@163.com>
> Signed-off-by: Shenghao Yuan <shenghaoyuan0928@163.com>
> Signed-off-by: Yazhou Tang <tangyazhou518@outlook.com>
> ---
>   .../selftests/bpf/prog_tests/verifier.c       |  2 +
>   .../bpf/progs/verifier_call_large_imm.c       | 66 +++++++++++++++++++
>   2 files changed, 68 insertions(+)
>   create mode 100644 tools/testing/selftests/bpf/progs/verifier_call_large_imm.c
> 
> diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
> index a96b25ebff23..b27f4d4e2bd4 100644
> --- a/tools/testing/selftests/bpf/prog_tests/verifier.c
> +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
> @@ -22,6 +22,7 @@
>   #include "verifier_bswap.skel.h"
>   #include "verifier_btf_ctx_access.skel.h"
>   #include "verifier_btf_unreliable_prog.skel.h"
> +#include "verifier_call_large_imm.skel.h"
>   #include "verifier_cfg.skel.h"
>   #include "verifier_cgroup_inv_retcode.skel.h"
>   #include "verifier_cgroup_skb.skel.h"
> @@ -170,6 +171,7 @@ void test_verifier_bpf_trap(void)             { RUN(verifier_bpf_trap); }
>   void test_verifier_bswap(void)                { RUN(verifier_bswap); }
>   void test_verifier_btf_ctx_access(void)       { RUN(verifier_btf_ctx_access); }
>   void test_verifier_btf_unreliable_prog(void)  { RUN(verifier_btf_unreliable_prog); }
> +void test_verifier_call_large_imm(void)       { RUN(verifier_call_large_imm); }
>   void test_verifier_cfg(void)                  { RUN(verifier_cfg); }
>   void test_verifier_cgroup_inv_retcode(void)   { RUN(verifier_cgroup_inv_retcode); }
>   void test_verifier_cgroup_skb(void)           { RUN(verifier_cgroup_skb); }
> diff --git a/tools/testing/selftests/bpf/progs/verifier_call_large_imm.c b/tools/testing/selftests/bpf/progs/verifier_call_large_imm.c
> new file mode 100644
> index 000000000000..1e2c8addc582
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/verifier_call_large_imm.c
> @@ -0,0 +1,66 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/bpf.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_misc.h"
> +
> +int call_happened = 0;
> +
> +/*
> + * 32765 is the exact minimum number of padding instructions needed to
> + * trigger the verifier failure, because:
> + * 1. Counting the wrapper instructions around the padding block (one
> + *    "r0=0" and two "exit" instructions), the actual jump distance
> + *    evaluates to N + 3.
> + * 2. To overflow the s16 max bound (32767), we need N + 3 > 32767.
> + * Thus, N = 32765 is the exact minimum padding size required.
> + */
> +static __attribute__((noinline)) void padding_subprog(void)
> +{
> +	asm volatile (
> +	"r0 = 0;"
> +	".rept 32765;"
> +	"r0 += 0;"
> +	".endr;"
> +	::: __clobber_all);
> +}
> +
> +static __attribute__((noinline)) int target_subprog(void)
> +{
> +	/* Use volatile variable here to prevent optimization. */
> +	volatile int magic_ret = 3;
> +	return magic_ret;
> +}
> +
> +SEC("syscall")
> +__success __retval(3)
> +int call_large_imm_test(void *ctx)
> +{
> +	/*
> +	 * Landing pad to handle call error on kernel without the fix,
> +	 * preventing kernel panic.
> +	 */
> +	asm volatile (
> +	"r0 = 0;"
> +	".rept 32768;"
> +	"r0 += 0;"
> +	".endr;"
> +	::: __clobber_all);
> +
> +	/*
> +	 * The call_happened variable is 1 only when the call insn wrongly
> +	 * go back to the landing pad above.
> +	 */
> +	if (call_happened == 1) {
> +		/* Use volatile variable here to prevent optimization. */
> +		volatile int flag = -1;
> +		return flag;
> +	}
> +
> +	call_happened = 1;
> +
> +	padding_subprog();
> +
> +	return target_subprog();
> +}
> +
> +char LICENSE[] SEC("license") = "GPL";

Acked-by: Xu Kuohai <xukuohai@huawei.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-04-29 21:10   ` sashiko-bot
@ 2026-04-30 14:47     ` Yazhou Tang
  0 siblings, 0 replies; 15+ messages in thread
From: Yazhou Tang @ 2026-04-30 14:47 UTC (permalink / raw)
  To: sashiko; +Cc: bpf



On 4/30/26 05:10, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] Unprivileged users will see broken control flow graphs and `call pc+0` in BPF-to-BPF pseudo calls.
> --
> 
> commit c7ae76817147356f188b1975260698d1d9dcd44a
> Author: Yazhou Tang <tangyazhou518@outlook.com>
> 
> bpf: Fix s16 truncation for large bpf-to-bpf call offsets
> 
> This commit fixes s16 truncation for large BPF-to-BPF call offsets by keeping
> the relative offset in the 32-bit imm field instead of downcasting it to the
> 16-bit off field. It updates the JIT, interpreter, and related dumper code to
> swap the usages of imm and off for BPF pseudo calls.
> 
>> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
>> index 300e4e251931..8947ef74f6a8 100644
>> --- a/kernel/bpf/fixups.c
>> +++ b/kernel/bpf/fixups.c
>> @@ -1250,9 +1250,9 @@ static int jit_subprogs(struct bpf_verifier_env *env)
>>   		}
>>   		if (!bpf_pseudo_call(insn))
>>   			continue;
>> -		insn->off = env->insn_aux_data[i].call_imm;
>> -		subprog = bpf_find_subprog(env, i + insn->off + 1);
>> -		insn->imm = subprog;
>> +		insn->imm = env->insn_aux_data[i].call_imm;
>> +		subprog = bpf_find_subprog(env, i + insn->imm + 1);
>> +		insn->off = subprog;
>>   	}
> 
> Because this patch swaps the usage of insn->imm and insn->off for BPF pseudo
> calls, will this cause a regression for unprivileged users dumping programs?
> 
> In kernel/bpf/syscall.c:bpf_insn_prepare_dump(), the kernel zeroes insn->imm
> for all BPF_CALL instructions when the user lacks privileges:
> 
>      if (code == (BPF_JMP | BPF_CALL)) {
>          insns[i].imm = 0;
>          ...
> 
> Previously, this only hid the subprogram index. Now that insn->imm holds the
> actual relative offset, zeroing it means tools like bpftool will receive an
> offset of 0.
> 
> Could this regression cause bpftool prog dump xlated to output call pc+0 and
> break the control flow graph generation for unprivileged users?
> 

OK, this problem is confirmed.

As root user, executing "sysctl -w kernel.kptr_restrict=2" before "bpftool
prog dump xlated" will indeed demonstrate the bug. I will change the
bpf_insn_prepare_dump() function in v10.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 1/3] bpf: Fix out-of-bounds read in bpf_patch_call_args()
  2026-04-30  3:29   ` Xu Kuohai
@ 2026-04-30 14:50     ` Yazhou Tang
  0 siblings, 0 replies; 15+ messages in thread
From: Yazhou Tang @ 2026-04-30 14:50 UTC (permalink / raw)
  To: Xu Kuohai, bpf, ast, eddyz87, emil, puranjay
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye

Hi Kuohai,

Thanks for your review and Acked-by tag.

On 4/30/26 11:29, Xu Kuohai wrote:
> On 4/30/2026 1:19 AM, Yazhou Tang wrote:
[...]>> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
>> index fba9e8c00878..300e4e251931 100644
>> --- a/kernel/bpf/fixups.c
>> +++ b/kernel/bpf/fixups.c
>> @@ -1416,7 +1416,11 @@ int bpf_fixup_call_args(struct bpf_verifier_env 
>> *env)
>>           depth = get_callee_stack_depth(env, insn, i);
>>           if (depth < 0)
>>               return depth;
>> -        bpf_patch_call_args(insn, depth);
>> +        err = bpf_patch_call_args(insn, depth);
>> +        if (err) {
>> +            verbose(env, "interpreter stack depth %d is too large\n", 
>> depth);
> 
> Nit: the log is misleading, change it to "stack depth %d exceeds 
> interpreter
> stack depth limit"?

Make sense. I'll update it in v10.

> 
>> +            return err;
>> +        }
>>       }
>>       err = 0;
>>   #endif
> 
> Aside from the the log wording, it looks good to me.
> 
> Acked-by: Xu Kuohai <xukuohai@huawei.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-04-30  3:29   ` Xu Kuohai
@ 2026-04-30 15:00     ` Yazhou Tang
  2026-04-30 15:25       ` Quentin Monnet
  0 siblings, 1 reply; 15+ messages in thread
From: Yazhou Tang @ 2026-04-30 15:00 UTC (permalink / raw)
  To: Xu Kuohai, bpf, ast, eddyz87, emil, puranjay
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye, Quentin Monnet

Hi Kuohai,

Thanks for your review and the Acked-by tag.

On 4/30/26 11:29, Xu Kuohai wrote:
> On 4/30/2026 1:19 AM, Yazhou Tang wrote:

[...]

>> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
>> index 300e4e251931..8947ef74f6a8 100644
>> --- a/kernel/bpf/fixups.c
>> +++ b/kernel/bpf/fixups.c
>> @@ -1250,9 +1250,9 @@ static int jit_subprogs(struct bpf_verifier_env 
>> *env)
>>           }
>>           if (!bpf_pseudo_call(insn))
>>               continue;
>> -        insn->off = env->insn_aux_data[i].call_imm;
>> -        subprog = bpf_find_subprog(env, i + insn->off + 1);
>> -        insn->imm = subprog;
>> +        insn->imm = env->insn_aux_data[i].call_imm;
>> +        subprog = bpf_find_subprog(env, i + insn->imm + 1);
>> +        insn->off = subprog;
>>       }
>>       prog->jited = 1;
>> diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
>> index e3785f9a697d..df43a0e0023f 100644
>> --- a/tools/bpf/bpftool/cfg.c
>> +++ b/tools/bpf/bpftool/cfg.c
>> @@ -142,7 +142,7 @@ static bool cfg_partition_funcs(struct cfg *cfg, 
>> struct bpf_insn *cur,
>>               continue;
>>           if (cur->src_reg != BPF_PSEUDO_CALL)
>>               continue;
>> -        func = cfg_append_func(cfg, cur + cur->off + 1);
>> +        func = cfg_append_func(cfg, cur + cur->imm + 1);
> 
> Do we need to matain backward compatibility with the old bpftool here?
> I suspect not, but I'm not entirely sure.

I agree. Since bpftool is maintained in-tree, it should be enough to just
keep it in sync with the current kernel tree.

> 
>>           if (!func)
>>               return true;
>>       }
>> diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/ 
>> xlated_dumper.c
>> index 5e7cb8b36fef..6ccda6787245 100644
>> --- a/tools/bpf/bpftool/xlated_dumper.c
>> +++ b/tools/bpf/bpftool/xlated_dumper.c
>> @@ -150,13 +150,13 @@ static const char *print_call_pcrel(struct 
>> dump_data *dd,
>>       if (!dd->nr_jited_ksyms)
>>           /* Do not show address for interpreted programs */
>>           snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
>> -            "%+d", insn->off);
>> +            "%+d", insn->imm);
>>       else if (sym)
>>           snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
>> -             "%+d#%s", insn->off, sym->name);
>> +             "%+d#%s", insn->imm, sym->name);
>>       else
>>           snprintf(dd->scratch_buff, sizeof(dd->scratch_buff),
>> -             "%+d#0x%lx", insn->off, address);
>> +             "%+d#0x%lx", insn->imm, address);
>>       return dd->scratch_buff;
>>   }
>> @@ -181,8 +181,8 @@ static const char *print_call(void *private_data,
>>       struct kernel_sym *sym;
>>       if (insn->src_reg == BPF_PSEUDO_CALL &&
>> -        (__u32) insn->imm < dd->nr_jited_ksyms && dd->jited_ksyms)
>> -        address = dd->jited_ksyms[insn->imm];
>> +        (__u32) insn->off < dd->nr_jited_ksyms && dd->jited_ksyms)
>> +        address = dd->jited_ksyms[insn->off];
>>       sym = kernel_syms_search(dd, address);
>>       if (insn->src_reg == BPF_PSEUDO_CALL)
>> diff --git a/tools/testing/selftests/bpf/disasm_helpers.c b/tools/ 
>> testing/selftests/bpf/disasm_helpers.c
>> index f529f1c8c171..96b1f2ffe438 100644
>> --- a/tools/testing/selftests/bpf/disasm_helpers.c
>> +++ b/tools/testing/selftests/bpf/disasm_helpers.c
>> @@ -4,7 +4,6 @@
>>   #include "disasm.h"
>>   struct print_insn_context {
>> -    char scratch[16];
>>       char *buf;
>>       size_t sz;
>>   };
>> @@ -19,22 +18,6 @@ static void print_insn_cb(void *private_data, const 
>> char *fmt, ...)
>>       va_end(args);
>>   }
>> -static const char *print_call_cb(void *private_data, const struct 
>> bpf_insn *insn)
>> -{
>> -    struct print_insn_context *ctx = private_data;
>> -
>> -    /* For pseudo calls verifier.c:jit_subprogs() hides original
>> -     * imm to insn->off and changes insn->imm to be an index of
>> -     * the subprog instead.
>> -     */
>> -    if (insn->src_reg == BPF_PSEUDO_CALL) {
>> -        snprintf(ctx->scratch, sizeof(ctx->scratch), "%+d", insn->off);
>> -        return ctx->scratch;
>> -    }
>> -
>> -    return NULL;
>> -}
>> -
>>   struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, 
>> size_t buf_sz)
>>   {
>>       struct print_insn_context ctx = {
>> @@ -43,7 +26,6 @@ struct bpf_insn *disasm_insn(struct bpf_insn *insn, 
>> char *buf, size_t buf_sz)
>>       };
>>       struct bpf_insn_cbs cbs = {
>>           .cb_print    = print_insn_cb,
>> -        .cb_call    = print_call_cb,
>>           .private_data    = &ctx,
>>       };
>>       char *tmp, *pfx_end, *sfx_start;
> 
> Aside from the potential bpftool compatibility issue, it looks good to me.
> 
> Acked-by: Xu Kuohai <xukuohai@huawei.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-04-30 15:00     ` Yazhou Tang
@ 2026-04-30 15:25       ` Quentin Monnet
  2026-05-01  7:35         ` Yazhou Tang
  0 siblings, 1 reply; 15+ messages in thread
From: Quentin Monnet @ 2026-04-30 15:25 UTC (permalink / raw)
  To: Yazhou Tang, Xu Kuohai, bpf, ast, eddyz87, emil, puranjay
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye

2026-04-30 23:00 UTC+0800 ~ Yazhou Tang <tangyazhou@zju.edu.cn>
> Hi Kuohai,
> 
> Thanks for your review and the Acked-by tag.
> 
> On 4/30/26 11:29, Xu Kuohai wrote:
>> On 4/30/2026 1:19 AM, Yazhou Tang wrote:
> 
> [...]
> 
>>> diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
>>> index 300e4e251931..8947ef74f6a8 100644
>>> --- a/kernel/bpf/fixups.c
>>> +++ b/kernel/bpf/fixups.c
>>> @@ -1250,9 +1250,9 @@ static int jit_subprogs(struct bpf_verifier_env
>>> *env)
>>>           }
>>>           if (!bpf_pseudo_call(insn))
>>>               continue;
>>> -        insn->off = env->insn_aux_data[i].call_imm;
>>> -        subprog = bpf_find_subprog(env, i + insn->off + 1);
>>> -        insn->imm = subprog;
>>> +        insn->imm = env->insn_aux_data[i].call_imm;
>>> +        subprog = bpf_find_subprog(env, i + insn->imm + 1);
>>> +        insn->off = subprog;
>>>       }
>>>       prog->jited = 1;
>>> diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
>>> index e3785f9a697d..df43a0e0023f 100644
>>> --- a/tools/bpf/bpftool/cfg.c
>>> +++ b/tools/bpf/bpftool/cfg.c
>>> @@ -142,7 +142,7 @@ static bool cfg_partition_funcs(struct cfg *cfg,
>>> struct bpf_insn *cur,
>>>               continue;
>>>           if (cur->src_reg != BPF_PSEUDO_CALL)
>>>               continue;
>>> -        func = cfg_append_func(cfg, cur + cur->off + 1);
>>> +        func = cfg_append_func(cfg, cur + cur->imm + 1);
>>
>> Do we need to matain backward compatibility with the old bpftool here?
>> I suspect not, but I'm not entirely sure.
> 
> I agree. Since bpftool is maintained in-tree, it should be enough to just
> keep it in sync with the current kernel tree.

Hi, bpftool is also mirrored on GitHub at
https://github.com/libbpf/bpftool
and many people build it from there, so yes, it should be backward
compatible if possible at all.

Thanks,
Quentin

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-04-30 15:25       ` Quentin Monnet
@ 2026-05-01  7:35         ` Yazhou Tang
  2026-05-01  7:53           ` Alexei Starovoitov
  0 siblings, 1 reply; 15+ messages in thread
From: Yazhou Tang @ 2026-05-01  7:35 UTC (permalink / raw)
  To: Quentin Monnet, Xu Kuohai, bpf, ast, eddyz87, emil, puranjay
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye

Hi Quentin,

Thanks for your reply.

On 4/30/26 23:25, Quentin Monnet wrote:
> 2026-04-30 23:00 UTC+0800 ~ Yazhou Tang <tangyazhou@zju.edu.cn>
>> Hi Kuohai,
>>
>> Thanks for your review and the Acked-by tag.
>>
>> On 4/30/26 11:29, Xu Kuohai wrote:
>>> On 4/30/2026 1:19 AM, Yazhou Tang wrote:
>>
>> [...]
>>
>>>> diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
>>>> index e3785f9a697d..df43a0e0023f 100644
>>>> --- a/tools/bpf/bpftool/cfg.c
>>>> +++ b/tools/bpf/bpftool/cfg.c
>>>> @@ -142,7 +142,7 @@ static bool cfg_partition_funcs(struct cfg *cfg,
>>>> struct bpf_insn *cur,
>>>>                continue;
>>>>            if (cur->src_reg != BPF_PSEUDO_CALL)
>>>>                continue;
>>>> -        func = cfg_append_func(cfg, cur + cur->off + 1);
>>>> +        func = cfg_append_func(cfg, cur + cur->imm + 1);
>>>
>>> Do we need to matain backward compatibility with the old bpftool here?
>>> I suspect not, but I'm not entirely sure.
>>
>> I agree. Since bpftool is maintained in-tree, it should be enough to just
>> keep it in sync with the current kernel tree.
> 
> Hi, bpftool is also mirrored on GitHub at
> https://github.com/libbpf/bpftool
> and many people build it from there, so yes, it should be backward
> compatible if possible at all.

Thanks for pointing that out. Considering this situation, I will make
sure to maintain backward compatibility in bpftool.

However, since the xlated format for BPF_PSEUDO_CALL swaps the imm and
off fields, distinguishing the old format from the new one purely from
the instruction stream seems a bit tricky. Both fields could technically
contain small integers.

I'm not very familiar with bpftool. Is there a standard/preferred way
in bpftool to probe for such kernel behavior changes?

One potential approach could be to load a minimal dummy BPF program with
a BPF_PSEUDO_CALL instruction, dump its xlated instructions, and check
whether the kernel stored the offset in imm or off. Finally use this
boolean result for subsequent bpftool dumps.

Do you have any suggestions on this? Thanks again for the guidance.

> 
> Thanks,
> Quentin


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-05-01  7:35         ` Yazhou Tang
@ 2026-05-01  7:53           ` Alexei Starovoitov
  2026-05-03  6:55             ` Yazhou Tang
  0 siblings, 1 reply; 15+ messages in thread
From: Alexei Starovoitov @ 2026-05-01  7:53 UTC (permalink / raw)
  To: Yazhou Tang
  Cc: Quentin Monnet, Xu Kuohai, bpf, Alexei Starovoitov, Eduard,
	Emil Tsalapatis, Puranjay Mohan, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Yazhou Tang,
	Shenghao Yuan, Tianci Cao

On Fri, May 1, 2026 at 8:36 AM Yazhou Tang <tangyazhou@zju.edu.cn> wrote:
>
> Hi Quentin,
>
> Thanks for your reply.
>
> On 4/30/26 23:25, Quentin Monnet wrote:
> > 2026-04-30 23:00 UTC+0800 ~ Yazhou Tang <tangyazhou@zju.edu.cn>
> >> Hi Kuohai,
> >>
> >> Thanks for your review and the Acked-by tag.
> >>
> >> On 4/30/26 11:29, Xu Kuohai wrote:
> >>> On 4/30/2026 1:19 AM, Yazhou Tang wrote:
> >>
> >> [...]
> >>
> >>>> diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
> >>>> index e3785f9a697d..df43a0e0023f 100644
> >>>> --- a/tools/bpf/bpftool/cfg.c
> >>>> +++ b/tools/bpf/bpftool/cfg.c
> >>>> @@ -142,7 +142,7 @@ static bool cfg_partition_funcs(struct cfg *cfg,
> >>>> struct bpf_insn *cur,
> >>>>                continue;
> >>>>            if (cur->src_reg != BPF_PSEUDO_CALL)
> >>>>                continue;
> >>>> -        func = cfg_append_func(cfg, cur + cur->off + 1);
> >>>> +        func = cfg_append_func(cfg, cur + cur->imm + 1);
> >>>
> >>> Do we need to matain backward compatibility with the old bpftool here?
> >>> I suspect not, but I'm not entirely sure.
> >>
> >> I agree. Since bpftool is maintained in-tree, it should be enough to just
> >> keep it in sync with the current kernel tree.
> >
> > Hi, bpftool is also mirrored on GitHub at
> > https://github.com/libbpf/bpftool
> > and many people build it from there, so yes, it should be backward
> > compatible if possible at all.
>
> Thanks for pointing that out. Considering this situation, I will make
> sure to maintain backward compatibility in bpftool.
>
> However, since the xlated format for BPF_PSEUDO_CALL swaps the imm and
> off fields, distinguishing the old format from the new one purely from
> the instruction stream seems a bit tricky. Both fields could technically
> contain small integers.
>
> I'm not very familiar with bpftool. Is there a standard/preferred way
> in bpftool to probe for such kernel behavior changes?
>
> One potential approach could be to load a minimal dummy BPF program with
> a BPF_PSEUDO_CALL instruction, dump its xlated instructions, and check
> whether the kernel stored the offset in imm or off. Finally use this
> boolean result for subsequent bpftool dumps.
>
> Do you have any suggestions on this? Thanks again for the guidance.

There should be no need to do probing in bpftool.
The xlated insns should be translated back to the original format
by the kernel.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-05-01  7:53           ` Alexei Starovoitov
@ 2026-05-03  6:55             ` Yazhou Tang
  0 siblings, 0 replies; 15+ messages in thread
From: Yazhou Tang @ 2026-05-03  6:55 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Quentin Monnet, Xu Kuohai, bpf, Alexei Starovoitov, Eduard,
	Emil Tsalapatis, Puranjay Mohan, Daniel Borkmann, John Fastabend,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Yazhou Tang,
	Shenghao Yuan, Tianci Cao

Hi Alexei,

Thanks for your advice.

On 5/1/26 15:53, Alexei Starovoitov wrote:
> On Fri, May 1, 2026 at 8:36 AM Yazhou Tang <tangyazhou@zju.edu.cn> wrote:
>>
>> Hi Quentin,
>>
>> Thanks for your reply.
>>
>> On 4/30/26 23:25, Quentin Monnet wrote:
>>> 2026-04-30 23:00 UTC+0800 ~ Yazhou Tang <tangyazhou@zju.edu.cn>
>>>> Hi Kuohai,
>>>>
>>>> Thanks for your review and the Acked-by tag.
>>>>
>>>> On 4/30/26 11:29, Xu Kuohai wrote:
>>>>> On 4/30/2026 1:19 AM, Yazhou Tang wrote:
>>>>
>>>> [...]
>>>>
>>>>>> diff --git a/tools/bpf/bpftool/cfg.c b/tools/bpf/bpftool/cfg.c
>>>>>> index e3785f9a697d..df43a0e0023f 100644
>>>>>> --- a/tools/bpf/bpftool/cfg.c
>>>>>> +++ b/tools/bpf/bpftool/cfg.c
>>>>>> @@ -142,7 +142,7 @@ static bool cfg_partition_funcs(struct cfg *cfg,
>>>>>> struct bpf_insn *cur,
>>>>>>                 continue;
>>>>>>             if (cur->src_reg != BPF_PSEUDO_CALL)
>>>>>>                 continue;
>>>>>> -        func = cfg_append_func(cfg, cur + cur->off + 1);
>>>>>> +        func = cfg_append_func(cfg, cur + cur->imm + 1);
>>>>>
>>>>> Do we need to matain backward compatibility with the old bpftool here?
>>>>> I suspect not, but I'm not entirely sure.
>>>>
>>>> I agree. Since bpftool is maintained in-tree, it should be enough to just
>>>> keep it in sync with the current kernel tree.
>>>
>>> Hi, bpftool is also mirrored on GitHub at
>>> https://github.com/libbpf/bpftool
>>> and many people build it from there, so yes, it should be backward
>>> compatible if possible at all.
>>
>> Thanks for pointing that out. Considering this situation, I will make
>> sure to maintain backward compatibility in bpftool.
>>
>> However, since the xlated format for BPF_PSEUDO_CALL swaps the imm and
>> off fields, distinguishing the old format from the new one purely from
>> the instruction stream seems a bit tricky. Both fields could technically
>> contain small integers.
>>
>> I'm not very familiar with bpftool. Is there a standard/preferred way
>> in bpftool to probe for such kernel behavior changes?
>>
>> One potential approach could be to load a minimal dummy BPF program with
>> a BPF_PSEUDO_CALL instruction, dump its xlated instructions, and check
>> whether the kernel stored the offset in imm or off. Finally use this
>> boolean result for subsequent bpftool dumps.
>>
>> Do you have any suggestions on this? Thanks again for the guidance.
> 
> There should be no need to do probing in bpftool.
> The xlated insns should be translated back to the original format
> by the kernel.

Got it. I will restore the original format for the xlated insns in
bpf_insn_prepare_dump() and drop the bpftool modifications.

One minor issue is that the printed offset will still be truncated
if the real offset (the original insn->imm) is out of s16 bounds.
To avoid confusion, I will just set it to 0 in that case. See prior
discussion in https://lore.kernel.org/bpf/422fce2b-228d-40d0-8fbf-f295f2aac0db@zju.edu.cn/

Thanks for your guidance.


^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-05-03  6:56 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-29 17:19 [PATCH bpf-next v9 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
2026-04-29 17:19 ` [PATCH bpf-next v9 1/3] bpf: Fix out-of-bounds " Yazhou Tang
2026-04-30  3:29   ` Xu Kuohai
2026-04-30 14:50     ` Yazhou Tang
2026-04-29 17:19 ` [PATCH bpf-next v9 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang
2026-04-29 21:10   ` sashiko-bot
2026-04-30 14:47     ` Yazhou Tang
2026-04-30  3:29   ` Xu Kuohai
2026-04-30 15:00     ` Yazhou Tang
2026-04-30 15:25       ` Quentin Monnet
2026-05-01  7:35         ` Yazhou Tang
2026-05-01  7:53           ` Alexei Starovoitov
2026-05-03  6:55             ` Yazhou Tang
2026-04-29 17:19 ` [PATCH bpf-next v9 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang
2026-04-30  3:30   ` Xu Kuohai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox