* [PATCH bpf v6 0/2] bpf: Fix s16 truncation for large bpf-to-bpf call offsets @ 2026-04-12 17:03 Yazhou Tang 2026-04-12 17:03 ` [PATCH bpf v6 1/2] " Yazhou Tang 2026-04-12 17:03 ` [PATCH bpf v6 2/2] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang 0 siblings, 2 replies; 7+ messages in thread From: Yazhou Tang @ 2026-04-12 17:03 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. 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 causes incorrect call targets or subprog ID resolution for large BPF programs. Patch 1/2 fixes the interpreter path by keeping the original imm field unchanged and using the off field to store the interpreter function index in bpf_patch_call_args(). It also adjusts the JMP_CALL_ARGS case in ___bpf_prog_run() and the dumper code accordingly, while removing a previous workaround in the selftests disasm helpers. Patch 2/2 introduces a selftest for this fix. When JIT is disabled, running the test without this fix triggers a kernel panic due to an invalid call target caused by the truncation issue. --- Change log: v6: 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 (2): bpf: Fix s16 truncation for large bpf-to-bpf call offsets selftests/bpf: Add test for large offset bpf-to-bpf call include/linux/filter.h | 3 -- kernel/bpf/core.c | 10 ++-- kernel/bpf/verifier.c | 6 +-- tools/bpf/bpftool/xlated_dumper.c | 10 ++-- tools/testing/selftests/bpf/disasm_helpers.c | 18 -------- .../selftests/bpf/prog_tests/call_large_imm.c | 29 ++++++++++++ .../selftests/bpf/progs/call_large_imm.c | 46 +++++++++++++++++++ 7 files changed, 88 insertions(+), 34 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/call_large_imm.c create mode 100644 tools/testing/selftests/bpf/progs/call_large_imm.c -- 2.53.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH bpf v6 1/2] bpf: Fix s16 truncation for large bpf-to-bpf call offsets 2026-04-12 17:03 [PATCH bpf v6 0/2] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang @ 2026-04-12 17:03 ` Yazhou Tang 2026-04-12 20:13 ` Alexei Starovoitov 2026-04-12 17:03 ` [PATCH bpf v6 2/2] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang 1 sibling, 1 reply; 7+ messages in thread From: Yazhou Tang @ 2026-04-12 17:03 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. 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> --- To Emil and Puranjay: The v6 of the patch is quite different from v1-v5, so I removed your "Reviewed-by" tags. Please take another look at v6. Thanks! include/linux/filter.h | 3 --- kernel/bpf/core.c | 10 +++++----- kernel/bpf/verifier.c | 6 +++--- tools/bpf/bpftool/xlated_dumper.c | 10 +++++----- tools/testing/selftests/bpf/disasm_helpers.c | 18 ------------------ 5 files changed, 13 insertions(+), 34 deletions(-) diff --git a/include/linux/filter.h b/include/linux/filter.h index 44d7ae95ddbc..559ddbc4fdfc 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1149,9 +1149,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_prog *prog); void bpf_jit_compile(struct bpf_prog *prog); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 9de5e69607c6..a35413c668c9 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1763,6 +1763,8 @@ 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 @@ -2069,10 +2071,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: { @@ -2405,9 +2407,7 @@ EVAL4(PROG_NAME_LIST, 416, 448, 480, 512) 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->off = (round_up(stack_depth, 32) / 32) - 1; insn->code = BPF_JMP | BPF_CALL_ARGS; } #endif diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e3814152b52f..1015b08d130e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -23054,9 +23054,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 = find_subprog(env, i + insn->off + 1); - insn->imm = subprog; + insn->imm = env->insn_aux_data[i].call_imm; + subprog = find_subprog(env, i + insn->imm + 1); + insn->off = subprog; } prog->jited = 1; 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.53.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH bpf v6 1/2] bpf: Fix s16 truncation for large bpf-to-bpf call offsets 2026-04-12 17:03 ` [PATCH bpf v6 1/2] " Yazhou Tang @ 2026-04-12 20:13 ` Alexei Starovoitov 2026-04-13 2:38 ` Yazhou Tang 0 siblings, 1 reply; 7+ messages in thread From: Alexei Starovoitov @ 2026-04-12 20:13 UTC (permalink / raw) To: Yazhou Tang Cc: bpf, Alexei Starovoitov, Eduard, Emil Tsalapatis, Puranjay Mohan, Xu Kuohai, 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 Sun, Apr 12, 2026 at 10:04 AM Yazhou Tang <tangyazhou@zju.edu.cn> 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. > > 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> > --- > To Emil and Puranjay: The v6 of the patch is quite different from v1-v5, > so I removed your "Reviewed-by" tags. Please take another look at v6. Thanks! > include/linux/filter.h | 3 --- > kernel/bpf/core.c | 10 +++++----- > kernel/bpf/verifier.c | 6 +++--- > tools/bpf/bpftool/xlated_dumper.c | 10 +++++----- > tools/testing/selftests/bpf/disasm_helpers.c | 18 ------------------ > 5 files changed, 13 insertions(+), 34 deletions(-) > > diff --git a/include/linux/filter.h b/include/linux/filter.h > index 44d7ae95ddbc..559ddbc4fdfc 100644 > --- a/include/linux/filter.h > +++ b/include/linux/filter.h > @@ -1149,9 +1149,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_prog *prog); > void bpf_jit_compile(struct bpf_prog *prog); > diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c > index 9de5e69607c6..a35413c668c9 100644 > --- a/kernel/bpf/core.c > +++ b/kernel/bpf/core.c > @@ -1763,6 +1763,8 @@ 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 > @@ -2069,10 +2071,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: { > @@ -2405,9 +2407,7 @@ EVAL4(PROG_NAME_LIST, 416, 448, 480, 512) > 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->off = (round_up(stack_depth, 32) / 32) - 1; > insn->code = BPF_JMP | BPF_CALL_ARGS; > } > #endif > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index e3814152b52f..1015b08d130e 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -23054,9 +23054,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 = find_subprog(env, i + insn->off + 1); > - insn->imm = subprog; > + insn->imm = env->insn_aux_data[i].call_imm; > + subprog = find_subprog(env, i + insn->imm + 1); > + insn->off = subprog; > } The patch doesn't apply anymore. This function was moved. Also see sashiko review. It flagged a real issue. pw-bot: cr ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf v6 1/2] bpf: Fix s16 truncation for large bpf-to-bpf call offsets 2026-04-12 20:13 ` Alexei Starovoitov @ 2026-04-13 2:38 ` Yazhou Tang 2026-04-20 9:21 ` Yazhou Tang 0 siblings, 1 reply; 7+ messages in thread From: Yazhou Tang @ 2026-04-13 2:38 UTC (permalink / raw) To: Alexei Starovoitov Cc: bpf, Alexei Starovoitov, Eduard, Emil Tsalapatis, Puranjay Mohan, Xu Kuohai, 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 review. On 4/13/26 4:13 AM, Alexei Starovoitov wrote: > The patch doesn't apply anymore. This function was moved. > > Also see sashiko review. It flagged a real issue. > > pw-bot: cr Regarding the apply failure: My patch was originally targeted at the bpf tree as a bugfix, where it applies cleanly. I see that commit f1606dd0ac49 renamed find_subprog() to bpf_find_subprog() in the bpf-next tree. Given this conflict, would you prefer I rebase and target bpf-next for the v7 submission, or should I keep targeting the bpf tree? Regarding the Sashiko review: The issue of stack_depth exceeding MAX_BPF_STACK during JIT fallback is indeed a pre-existing bug (it originally caused a load-time OOB read). However, my patch inadvertently turned it into a more critical run-time OOB read. I will fix this properly in v7. I plan to change the return type of bpf_patch_call_args() to int and add a strict boundary check of stack_depth to safely reject the fallback. Thanks, Yazhou ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf v6 1/2] bpf: Fix s16 truncation for large bpf-to-bpf call offsets 2026-04-13 2:38 ` Yazhou Tang @ 2026-04-20 9:21 ` Yazhou Tang 2026-04-20 15:05 ` Alexei Starovoitov 0 siblings, 1 reply; 7+ messages in thread From: Yazhou Tang @ 2026-04-20 9:21 UTC (permalink / raw) To: Alexei Starovoitov Cc: bpf, Alexei Starovoitov, Eduard, Emil Tsalapatis, Puranjay Mohan, Xu Kuohai, 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, A gentle ping on this thread. If you don't have a strong preference regarding the target tree, my plan is to rebase this patchset onto the bpf-next tree to resolve the conflict with bpf_find_subprog(). I will also include the fix for the run-time OOB read caught by Sashiko, and send out v7 later this week. Please let me know if you'd prefer me to take a different approach. Thanks, Yazhou On 4/13/26 10:38 AM, Yazhou Tang wrote: > Hi Alexei, > > Thanks for your review. > > On 4/13/26 4:13 AM, Alexei Starovoitov wrote: >> The patch doesn't apply anymore. This function was moved. >> >> Also see sashiko review. It flagged a real issue. >> >> pw-bot: cr > > Regarding the apply failure: My patch was originally targeted at the bpf > tree as a bugfix, where it applies cleanly. I see that commit f1606dd0ac49 > renamed find_subprog() to bpf_find_subprog() in the bpf-next tree. > > Given this conflict, would you prefer I rebase and target bpf-next for the > v7 submission, or should I keep targeting the bpf tree? > > Regarding the Sashiko review: The issue of stack_depth exceeding MAX_BPF_STACK > during JIT fallback is indeed a pre-existing bug (it originally caused a > load-time OOB read). However, my patch inadvertently turned it into a more > critical run-time OOB read. > > I will fix this properly in v7. I plan to change the return type of > bpf_patch_call_args() to int and add a strict boundary check of stack_depth > to safely reject the fallback. > > Thanks, > Yazhou ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf v6 1/2] bpf: Fix s16 truncation for large bpf-to-bpf call offsets 2026-04-20 9:21 ` Yazhou Tang @ 2026-04-20 15:05 ` Alexei Starovoitov 0 siblings, 0 replies; 7+ messages in thread From: Alexei Starovoitov @ 2026-04-20 15:05 UTC (permalink / raw) To: Yazhou Tang Cc: bpf, Alexei Starovoitov, Eduard, Emil Tsalapatis, Puranjay Mohan, Xu Kuohai, 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 Mon Apr 20, 2026 at 2:21 AM PDT, Yazhou Tang wrote: > Hi Alexei, > > A gentle ping on this thread. Do not top post. > If you don't have a strong preference regarding the target tree, > my plan is to rebase this patchset onto the bpf-next tree to > resolve the conflict with bpf_find_subprog(). > > I will also include the fix for the run-time OOB read caught by > Sashiko, and send out v7 later this week. Please let me know if > you'd prefer me to take a different approach. yes. thanks ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH bpf v6 2/2] selftests/bpf: Add test for large offset bpf-to-bpf call 2026-04-12 17:03 [PATCH bpf v6 0/2] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang 2026-04-12 17:03 ` [PATCH bpf v6 1/2] " Yazhou Tang @ 2026-04-12 17:03 ` Yazhou Tang 1 sibling, 0 replies; 7+ messages in thread From: Yazhou Tang @ 2026-04-12 17:03 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. - 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 causes a kernel panic due to an invalid jump target. - 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. 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/call_large_imm.c | 29 ++++++++++++ .../selftests/bpf/progs/call_large_imm.c | 46 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/call_large_imm.c create mode 100644 tools/testing/selftests/bpf/progs/call_large_imm.c diff --git a/tools/testing/selftests/bpf/prog_tests/call_large_imm.c b/tools/testing/selftests/bpf/prog_tests/call_large_imm.c new file mode 100644 index 000000000000..3ed61a8ca08c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/call_large_imm.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <test_progs.h> +#include "call_large_imm.skel.h" + +void test_call_large_imm(void) +{ + struct call_large_imm *skel; + int err, prog_fd; + + LIBBPF_OPTS(bpf_test_run_opts, opts); + + skel = call_large_imm__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + err = call_large_imm__load(skel); + + if (!ASSERT_OK(err, "load_should_succeed")) + goto cleanup; + + prog_fd = bpf_program__fd(skel->progs.call_large_imm_test); + err = bpf_prog_test_run_opts(prog_fd, &opts); + + if (ASSERT_OK(err, "prog_run_success")) + ASSERT_EQ(opts.retval, 3, "prog_retval"); + +cleanup: + call_large_imm__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/progs/call_large_imm.c b/tools/testing/selftests/bpf/progs/call_large_imm.c new file mode 100644 index 000000000000..7cdf1e6d80d8 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/call_large_imm.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/bpf.h> +#include <bpf/bpf_helpers.h> + +/* + * A volatile global variable is used here, so that padding_subprog() + * will not be optimized, and it will not be really executed even if + * it is successfully loaded (when JIT is enabled). + */ +volatile int zero = 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; \ + " ::: "r0"); +} + +static __attribute__((noinline)) int target_subprog(void) +{ + /* A volatile variable is used here to prevent optimization. */ + volatile int magic_ret = 3; + return magic_ret; +} + +SEC("syscall") +int call_large_imm_test(void *ctx) +{ + if (zero) + padding_subprog(); + return target_subprog(); +} + +char LICENSE[] SEC("license") = "GPL"; -- 2.53.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-04-20 15:05 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-04-12 17:03 [PATCH bpf v6 0/2] bpf: Fix s16 truncation for large bpf-to-bpf call offsets Yazhou Tang 2026-04-12 17:03 ` [PATCH bpf v6 1/2] " Yazhou Tang 2026-04-12 20:13 ` Alexei Starovoitov 2026-04-13 2:38 ` Yazhou Tang 2026-04-20 9:21 ` Yazhou Tang 2026-04-20 15:05 ` Alexei Starovoitov 2026-04-12 17:03 ` [PATCH bpf v6 2/2] selftests/bpf: Add test for large offset bpf-to-bpf call Yazhou Tang
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox