BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args()
@ 2026-05-06  9:47 Yazhou Tang
  2026-05-06  9:47 ` [PATCH bpf-next v10 1/3] bpf: Fix out-of-bounds " Yazhou Tang
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Yazhou Tang @ 2026-05-06  9:47 UTC (permalink / raw)
  To: bpf, ast, eddyz87, emil, puranjay, xukuohai, qmo
  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.
1. Keep the original imm field unchanged and use the off field to store
   the interpreter function index.
2. Adjust the JMP_CALL_ARGS case in ___bpf_prog_run() accordingly.
3. Restore the legacy xlated dump layout in bpf_insn_prepare_dump().

Patch 3/3 introduces a selftest for this fix.

---

Change log:

v10:
1. Make the error log in patch 1/3 more clear. (Kuohai)
2. Drop bpftool and disasm_helpers.c changes, and instead restore the
   legacy xlated dump layout in bpf_insn_prepare_dump(). This avoids
   requiring bpftool compatibility handling. (Quentin and Alexei)

v9: https://lore.kernel.org/bpf/20260429171904.107244-1-tangyazhou@zju.edu.cn/
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                           |  8 ++-
 include/linux/filter.h                        |  3 -
 kernel/bpf/core.c                             | 27 +++++---
 kernel/bpf/fixups.c                           | 13 ++--
 kernel/bpf/syscall.c                          | 26 ++++++++
 .../selftests/bpf/prog_tests/verifier.c       |  2 +
 .../bpf/progs/verifier_call_large_imm.c       | 66 +++++++++++++++++++
 7 files changed, 129 insertions(+), 16 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/verifier_call_large_imm.c

-- 
2.54.0


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

* [PATCH bpf-next v10 1/3] bpf: Fix out-of-bounds read in bpf_patch_call_args()
  2026-05-06  9:47 [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
@ 2026-05-06  9:47 ` Yazhou Tang
  2026-05-06  9:47 ` [PATCH bpf-next v10 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Yazhou Tang @ 2026-05-06  9:47 UTC (permalink / raw)
  To: bpf, ast, eddyz87, emil, puranjay, xukuohai, qmo
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye, Xu Kuohai

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>
Acked-by: Xu Kuohai <xukuohai@huawei.com>
---
 include/linux/bpf.h | 2 +-
 kernel/bpf/core.c   | 6 +++++-
 kernel/bpf/fixups.c | 7 ++++++-
 3 files changed, 12 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..df8f48091321 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -1416,7 +1416,12 @@ 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, "stack depth %d exceeds interpreter stack depth limit\n",
+				depth);
+			return err;
+		}
 	}
 	err = 0;
 #endif
-- 
2.54.0


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

* [PATCH bpf-next v10 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
  2026-05-06  9:47 [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
  2026-05-06  9:47 ` [PATCH bpf-next v10 1/3] bpf: Fix out-of-bounds " Yazhou Tang
@ 2026-05-06  9:47 ` Yazhou Tang
  2026-05-06  9:47 ` [PATCH bpf-next v10 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Yazhou Tang @ 2026-05-06  9:47 UTC (permalink / raw)
  To: bpf, ast, eddyz87, emil, puranjay, xukuohai, qmo
  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., a jump offset greater
than 32767 instructions), 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/

To avoid requiring bpftool changes, keep the new imm/off encoding internal
and restore the legacy xlated dump layout in bpf_insn_prepare_dump().
For bpf-to-bpf call offsets that do not fit in s16, export off as 0 instead
of a truncated and misleading value.

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/bpf.h    |  6 ++++++
 include/linux/filter.h |  3 ---
 kernel/bpf/core.c      | 21 ++++++++++++++-------
 kernel/bpf/fixups.c    |  6 +++---
 kernel/bpf/syscall.c   | 26 ++++++++++++++++++++++++++
 5 files changed, 49 insertions(+), 13 deletions(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index eb6b816bc8b5..4e7f28538b17 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2923,6 +2923,12 @@ int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr, u32 ua
 
 #ifndef CONFIG_BPF_JIT_ALWAYS_ON
 int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
+s32 bpf_call_args_imm(s16 idx);
+#else
+static inline s32 bpf_call_args_imm(s16 idx)
+{
+	return 0;
+}
 #endif
 
 struct btf *bpf_get_btf_vmlinux(void);
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..bd80f1768ef6 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,9 @@ 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_R3, BPF_R4,
-							    BPF_R5,
-							    insn + insn->off + 1);
+		BPF_R0 = interpreters_args[insn->off](BPF_R1, BPF_R2, BPF_R3,
+						      BPF_R4, BPF_R5,
+						      insn + insn->imm + 1);
 		CONT;
 
 	JMP_TAIL_CALL: {
@@ -2400,12 +2402,17 @@ 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;
 }
+
+s32 bpf_call_args_imm(s16 idx)
+{
+	if (WARN_ON_ONCE(idx < 0 || idx >= ARRAY_SIZE(interpreters_args)))
+		return 0;
+	return BPF_CALL_IMM(interpreters_args[idx]);
+}
 #endif
 #endif
 
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index df8f48091321..3692adf62558 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/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 3b1f0ba02f61..f2e2aae5a1c1 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -4924,6 +4924,29 @@ static const struct bpf_map *bpf_map_from_imm(const struct bpf_prog *prog,
 	return map;
 }
 
+static void prepare_dump_pseudo_call(struct bpf_insn *insn)
+{
+	s32 call_off = insn->imm;
+
+	/*
+	 * BPF_CALL_ARGS only exists for interpreter fallback.
+	 * 1. For interpreter (BPF_CALL_ARGS): insn->off is the index of
+	 *    interpreters_args array, so here using bpf_call_args_imm()
+	 *    to get the real address offset.
+	 * 2. For JIT (BPF_CALL): insn->off is the subprog id.
+	 */
+	if (insn->code == (BPF_JMP | BPF_CALL_ARGS))
+		insn->imm = bpf_call_args_imm(insn->off);
+	else
+		insn->imm = insn->off;
+
+	/* Avoid dumping a truncated and misleading pc-relative offset. */
+	if (call_off > S16_MAX || call_off < S16_MIN)
+		insn->off = 0;
+	else
+		insn->off = call_off;
+}
+
 static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog,
 					      const struct cred *f_cred)
 {
@@ -4949,6 +4972,9 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog,
 		}
 		if (code == (BPF_JMP | BPF_CALL) ||
 		    code == (BPF_JMP | BPF_CALL_ARGS)) {
+			/* Restore the legacy xlated dump layout. */
+			if (insns[i].src_reg == BPF_PSEUDO_CALL)
+				prepare_dump_pseudo_call(&insns[i]);
 			if (code == (BPF_JMP | BPF_CALL_ARGS))
 				insns[i].code = BPF_JMP | BPF_CALL;
 			if (!bpf_dump_raw_ok(f_cred))
-- 
2.54.0


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

* [PATCH bpf-next v10 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call
  2026-05-06  9:47 [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
  2026-05-06  9:47 ` [PATCH bpf-next v10 1/3] bpf: Fix out-of-bounds " Yazhou Tang
  2026-05-06  9:47 ` [PATCH bpf-next v10 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang
@ 2026-05-06  9:47 ` Yazhou Tang
  2026-05-11  8:28 ` [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
  2026-05-11 15:40 ` patchwork-bot+netdevbpf
  4 siblings, 0 replies; 6+ messages in thread
From: Yazhou Tang @ 2026-05-06  9:47 UTC (permalink / raw)
  To: bpf, ast, eddyz87, emil, puranjay, xukuohai, qmo
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928,
	ziye, Xu Kuohai

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 the fix
  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
    (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>
Acked-by: Xu Kuohai <xukuohai@huawei.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..06cd24e37b3f 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..7998df07f6a6
--- /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] 6+ messages in thread

* Re: [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args()
  2026-05-06  9:47 [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
                   ` (2 preceding siblings ...)
  2026-05-06  9:47 ` [PATCH bpf-next v10 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang
@ 2026-05-11  8:28 ` Yazhou Tang
  2026-05-11 15:40 ` patchwork-bot+netdevbpf
  4 siblings, 0 replies; 6+ messages in thread
From: Yazhou Tang @ 2026-05-11  8:28 UTC (permalink / raw)
  To: bpf, ast, eddyz87, emil, puranjay, xukuohai, qmo
  Cc: daniel, john.fastabend, andrii, martin.lau, song, yonghong.song,
	kpsingh, sdf, haoluo, jolsa, shenghaoyuan0928, ziye

Hi all,

On 5/6/26 5:47 PM, Yazhou Tang wrote:
> 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.
> 1. Keep the original imm field unchanged and use the off field to store
>     the interpreter function index.
> 2. Adjust the JMP_CALL_ARGS case in ___bpf_prog_run() accordingly.
> 3. Restore the legacy xlated dump layout in bpf_insn_prepare_dump().
> 
> Patch 3/3 introduces a selftest for this fix.
> 
> ---
> 
> Change log:
> 
> v10:
> 1. Make the error log in patch 1/3 more clear. (Kuohai)
> 2. Drop bpftool and disasm_helpers.c changes, and instead restore the
>     legacy xlated dump layout in bpf_insn_prepare_dump(). This avoids
>     requiring bpftool compatibility handling. (Quentin and Alexei)
> 
> v9: https://lore.kernel.org/bpf/20260429171904.107244-1-tangyazhou@zju.edu.cn/
> 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                           |  8 ++-
>   include/linux/filter.h                        |  3 -
>   kernel/bpf/core.c                             | 27 +++++---
>   kernel/bpf/fixups.c                           | 13 ++--
>   kernel/bpf/syscall.c                          | 26 ++++++++
>   .../selftests/bpf/prog_tests/verifier.c       |  2 +
>   .../bpf/progs/verifier_call_large_imm.c       | 66 +++++++++++++++++++
>   7 files changed, 129 insertions(+), 16 deletions(-)
>   create mode 100644 tools/testing/selftests/bpf/progs/verifier_call_large_imm.c
> 

The GitHub CI failure on this submission appears unrelated to this series.
I re-ran the CI manually through a PR and it passed:

https://github.com/kernel-patches/bpf/pull/11984

Just wanted to clarify the CI status. Please let me know if anything else
is needed from my side.

Thanks,
Yazhou


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

* Re: [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args()
  2026-05-06  9:47 [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
                   ` (3 preceding siblings ...)
  2026-05-11  8:28 ` [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
@ 2026-05-11 15:40 ` patchwork-bot+netdevbpf
  4 siblings, 0 replies; 6+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-05-11 15:40 UTC (permalink / raw)
  To: Yazhou Tang
  Cc: bpf, ast, eddyz87, emil, puranjay, xukuohai, qmo, daniel,
	john.fastabend, andrii, martin.lau, song, yonghong.song, kpsingh,
	sdf, haoluo, jolsa, tangyazhou518, shenghaoyuan0928, ziye

Hello:

This series was applied to bpf/bpf.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Wed,  6 May 2026 17:47:11 +0800 you wrote:
> 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.
> 
> [...]

Here is the summary with links:
  - [bpf-next,v10,1/3] bpf: Fix out-of-bounds read in bpf_patch_call_args()
    https://git.kernel.org/bpf/bpf/c/4314a44564eb
  - [bpf-next,v10,2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets
    https://git.kernel.org/bpf/bpf/c/58a8f3e2501d
  - [bpf-next,v10,3/3] selftests/bpf: Add test for large offset bpf-to-bpf call
    https://git.kernel.org/bpf/bpf/c/344a00712ce1

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



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

end of thread, other threads:[~2026-05-11 15:41 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-06  9:47 [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
2026-05-06  9:47 ` [PATCH bpf-next v10 1/3] bpf: Fix out-of-bounds " Yazhou Tang
2026-05-06  9:47 ` [PATCH bpf-next v10 2/3] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang
2026-05-06  9:47 ` [PATCH bpf-next v10 3/3] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang
2026-05-11  8:28 ` [PATCH bpf-next v10 0/3] bpf: Fix call offset truncation and OOB read in bpf_patch_call_args() Yazhou Tang
2026-05-11 15:40 ` patchwork-bot+netdevbpf

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