* [PATCH bpf-next v3 0/2] bpf: Enforce gotox targets against subprog bounds
@ 2026-06-28 13:59 Nuoqi Gui
2026-06-28 13:59 ` [PATCH bpf-next v3 1/2] " Nuoqi Gui
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Nuoqi Gui @ 2026-06-28 13:59 UTC (permalink / raw)
To: bpf, John Fastabend, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Anton Protopopov, Shuah Khan, linux-kselftest,
linux-kernel, Nuoqi Gui
For gotox, CFG construction models the indirect-jump target set in
insn_aux_data->jt, while do_check() later follows targets from the runtime
PTR_TO_INSN register's own INSN_ARRAY map. If the same gotox can be
reached with PTR_TO_INSN values from different maps, do_check() can accept
a target outside the calling subprog.
The observed x86 JIT case can then enter another subprog without a matching
BPF call frame and crash when the program is run.
Fix this by rejecting gotox map targets outside the current gotox subprog.
Add a regression test covering the two-map cross-subprog case.
v1 -> v2:
- Validate gotox runtime targets against the current subprog bounds instead
of scanning the CFG jump table.
- Fix the selftest expected error from -EACCES to -EINVAL.
v2 -> v3:
- Drop the Validation section from the cover letter.
- Clarify that the crash was observed through the x86 JIT path while the
verifier invariant is generic.
- Simplify the cover letter and commit message.
- Remove the unused skel argument from the raw-insn selftest.
- Move the raw-insn selftest to the end of test_bpf_gotox().
v1:
https://lore.kernel.org/bpf/20260609-f01-03-gotox-bpf-next-v1-0-b441d63a1559@mails.tsinghua.edu.cn/
v2:
https://lore.kernel.org/bpf/20260613-f01-03-gotox-bpf-next-v2-send-v2-0-7c883b43f3c3@mails.tsinghua.edu.cn/
Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn>
---
Nuoqi Gui (2):
bpf: Enforce gotox targets against subprog bounds
selftests/bpf: Add cross-subprog gotox target coverage
kernel/bpf/verifier.c | 19 ++++++
tools/testing/selftests/bpf/prog_tests/bpf_gotox.c | 73 ++++++++++++++++++++++
2 files changed, 92 insertions(+)
---
base-commit: 7bfb93e3475be9de894f1cecd3a727d3e1649b03
change-id: 20260628-f01-03-gotox-bpf-next-1a7af91d2c82
Best regards,
--
Nuoqi Gui <gnq25@mails.tsinghua.edu.cn>
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH bpf-next v3 1/2] bpf: Enforce gotox targets against subprog bounds 2026-06-28 13:59 [PATCH bpf-next v3 0/2] bpf: Enforce gotox targets against subprog bounds Nuoqi Gui @ 2026-06-28 13:59 ` Nuoqi Gui 2026-06-28 14:24 ` sashiko-bot 2026-06-30 18:42 ` Anton Protopopov 2026-06-28 13:59 ` [PATCH bpf-next v3 2/2] selftests/bpf: Add cross-subprog gotox target coverage Nuoqi Gui 2026-06-30 18:21 ` [PATCH bpf-next v3 0/2] bpf: Enforce gotox targets against subprog bounds Anton Protopopov 2 siblings, 2 replies; 8+ messages in thread From: Nuoqi Gui @ 2026-06-28 13:59 UTC (permalink / raw) To: bpf, John Fastabend, Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Anton Protopopov, Shuah Khan, linux-kselftest, linux-kernel, Nuoqi Gui During CFG construction, the verifier records the modeled gotox target set in insn_aux_data->jt. Later, check_indirect_jump() follows targets from the runtime PTR_TO_INSN register's actual INSN_ARRAY map. This lets one gotox instruction observe different INSN_ARRAY maps on different paths and accept a target outside the calling subprog. The observed x86 JIT case can then enter another subprog without a matching BPF call frame and crash when executed. Reject every target copied from the actual PTR_TO_INSN map if it is outside the calling subprog. Fixes: 493d9e0d6083 ("bpf, x86: add support for indirect jumps") Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> --- kernel/bpf/verifier.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index eb46a81a8c51..05a996a5ecdd 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -17145,9 +17145,11 @@ static int indirect_jump_min_max_index(struct bpf_verifier_env *env, static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *insn) { struct bpf_verifier_state *other_branch; + struct bpf_subprog_info *subprog; struct bpf_reg_state *dst_reg; struct bpf_map *map; u32 min_index, max_index; + int subprog_start, subprog_end; int err = 0; int n; int i; @@ -17188,6 +17190,23 @@ static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *in return -EINVAL; } + subprog = bpf_find_containing_subprog(env, env->insn_idx); + if (verifier_bug_if(!subprog, env, + "gotox insn %d is outside subprog bounds\n", + env->insn_idx)) + return -EFAULT; + subprog_start = subprog->start; + subprog_end = (subprog + 1)->start; + + for (i = 0; i < n; i++) { + u32 target = env->gotox_tmp_buf->items[i]; + + if (target < subprog_start || target >= subprog_end) { + verbose(env, "gotox target %u outside subprog\n", target); + return -EINVAL; + } + } + for (i = 0; i < n - 1; i++) { mark_indirect_target(env, env->gotox_tmp_buf->items[i]); other_branch = push_stack(env, env->gotox_tmp_buf->items[i], -- 2.34.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v3 1/2] bpf: Enforce gotox targets against subprog bounds 2026-06-28 13:59 ` [PATCH bpf-next v3 1/2] " Nuoqi Gui @ 2026-06-28 14:24 ` sashiko-bot 2026-06-30 18:30 ` Anton Protopopov 2026-06-30 18:42 ` Anton Protopopov 1 sibling, 1 reply; 8+ messages in thread From: sashiko-bot @ 2026-06-28 14:24 UTC (permalink / raw) To: Nuoqi Gui; +Cc: bpf Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [Critical] The patch adds a runtime bounds check to prevent `gotox` targets from jumping outside the current subprogram, but fails to ensure that dynamically observed targets were actually modeled during Control Flow Graph (CFG) construction. This allows an attacker to follow unmodeled control-flow edges and bypass static liveness-based state pruning. -- commit 5e0bdd5b4a7e2ce073913482349669d9387d6a32 Author: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> bpf: Enforce gotox targets against subprog bounds During CFG construction, the verifier records the modeled gotox target set in insn_aux_data->jt. This patch adds a check to reject targets outside the calling subprogram bounds when check_indirect_jump() follows targets from the runtime PTR_TO_INSN register's actual INSN_ARRAY map. > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c [ ... ] > @@ -17188,6 +17190,23 @@ static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *in > return -EINVAL; > } > > + subprog = bpf_find_containing_subprog(env, env->insn_idx); > + if (verifier_bug_if(!subprog, env, > + "gotox insn %d is outside subprog bounds\n", > + env->insn_idx)) > + return -EFAULT; > + subprog_start = subprog->start; > + subprog_end = (subprog + 1)->start; > + > + for (i = 0; i < n; i++) { > + u32 target = env->gotox_tmp_buf->items[i]; > + > + if (target < subprog_start || target >= subprog_end) { [Severity: Critical] This is a pre-existing issue, but does this bounds check alone fully protect against unmodeled control-flow edges? During CFG construction in kernel/bpf/cfg.c:jt_from_subprog(), if an INSN_ARRAY map's minimum offset falls outside the subprogram, the heuristic skips the map entirely: if (jt_cur->items[0] >= subprog_start && jt_cur->items[0] < subprog_end) { If an attacker creates an INSN_ARRAY map containing both an out-of-bounds offset (e.g., 0) and a valid in-bounds target Y, the CFG construction drops Y completely because items[0] is less than subprog_start. Later at runtime, if the attacker restricts the bounds to index exactly Y, this check passes because Y is within subprog_start and subprog_end. The verifier then explores the gotox to Y edge that was never modeled in the CFG jump tables. Because liveness analysis relies strictly on the statically modeled CFG, could traversing this unmodeled edge cause liveness to incorrectly assume registers are dead, allowing an unsound state pruning bypass? Should this logic verify that the dynamic target is actually present in env->insn_aux_data[env->insn_idx].jt, rather than only verifying the target is within subprogram boundaries? > + verbose(env, "gotox target %u outside subprog\n", target); > + return -EINVAL; > + } > + } > + -- Sashiko AI review · https://sashiko.dev/#/patchset/20260628-f01-03-gotox-bpf-next-v3-0-b744432e1361@mails.tsinghua.edu.cn?part=1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v3 1/2] bpf: Enforce gotox targets against subprog bounds 2026-06-28 14:24 ` sashiko-bot @ 2026-06-30 18:30 ` Anton Protopopov 0 siblings, 0 replies; 8+ messages in thread From: Anton Protopopov @ 2026-06-30 18:30 UTC (permalink / raw) To: sashiko-reviews; +Cc: Nuoqi Gui, bpf On 26/06/28 02:24PM, sashiko-bot@kernel.org wrote: > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: > > Pre-existing issues: > - [Critical] The patch adds a runtime bounds check to prevent `gotox` targets from jumping outside the current subprogram, but fails to ensure that dynamically observed targets were actually modeled during Control Flow Graph (CFG) construction. This allows an attacker to follow unmodeled control-flow edges and bypass static liveness-based state pruning. > -- > > commit 5e0bdd5b4a7e2ce073913482349669d9387d6a32 > Author: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> > > bpf: Enforce gotox targets against subprog bounds > > During CFG construction, the verifier records the modeled gotox target set > in insn_aux_data->jt. This patch adds a check to reject targets outside the > calling subprogram bounds when check_indirect_jump() follows targets from > the runtime PTR_TO_INSN register's actual INSN_ARRAY map. > > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > > --- a/kernel/bpf/verifier.c > > +++ b/kernel/bpf/verifier.c > [ ... ] > > @@ -17188,6 +17190,23 @@ static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *in > > return -EINVAL; > > } > > > > + subprog = bpf_find_containing_subprog(env, env->insn_idx); > > + if (verifier_bug_if(!subprog, env, > > + "gotox insn %d is outside subprog bounds\n", > > + env->insn_idx)) > > + return -EFAULT; > > + subprog_start = subprog->start; > > + subprog_end = (subprog + 1)->start; > > + > > + for (i = 0; i < n; i++) { > > + u32 target = env->gotox_tmp_buf->items[i]; > > + > > + if (target < subprog_start || target >= subprog_end) { > > [Severity: Critical] > This is a pre-existing issue, but does this bounds check alone fully protect > against unmodeled control-flow edges? > > During CFG construction in kernel/bpf/cfg.c:jt_from_subprog(), if an > INSN_ARRAY map's minimum offset falls outside the subprogram, the heuristic > skips the map entirely: > > if (jt_cur->items[0] >= subprog_start && jt_cur->items[0] < subprog_end) { > > If an attacker creates an INSN_ARRAY map containing both an out-of-bounds > offset (e.g., 0) and a valid in-bounds target Y, the CFG construction drops > Y completely because items[0] is less than subprog_start. > > Later at runtime, if the attacker restricts the bounds to index exactly Y, > this check passes because Y is within subprog_start and subprog_end. The > verifier then explores the gotox to Y edge that was never modeled in the > CFG jump tables. The robot is right here, the check should verify that the map was actually checked during the config stage. I think the easiest way to check this is do the same check: verify that the whole map fits within the subprogram. (An optimization would be to cache this info somehow. Every jump table map can only lie within one subprog, so subprog index cached is enough.) Could you please address this and add a corresponding selftest? You also have lost some acks for this patch from v2, please don't forget to add them the next time. > Because liveness analysis relies strictly on the statically modeled CFG, > could traversing this unmodeled edge cause liveness to incorrectly assume > registers are dead, allowing an unsound state pruning bypass? > > Should this logic verify that the dynamic target is actually present in > env->insn_aux_data[env->insn_idx].jt, rather than only verifying the target > is within subprogram boundaries? > > > + verbose(env, "gotox target %u outside subprog\n", target); > > + return -EINVAL; > > + } > > + } > > + > > -- > Sashiko AI review · https://sashiko.dev/#/patchset/20260628-f01-03-gotox-bpf-next-v3-0-b744432e1361@mails.tsinghua.edu.cn?part=1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v3 1/2] bpf: Enforce gotox targets against subprog bounds 2026-06-28 13:59 ` [PATCH bpf-next v3 1/2] " Nuoqi Gui 2026-06-28 14:24 ` sashiko-bot @ 2026-06-30 18:42 ` Anton Protopopov 1 sibling, 0 replies; 8+ messages in thread From: Anton Protopopov @ 2026-06-30 18:42 UTC (permalink / raw) To: Nuoqi Gui Cc: bpf, John Fastabend, Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Shuah Khan, linux-kselftest, linux-kernel On 26/06/28 09:59PM, Nuoqi Gui wrote: > During CFG construction, the verifier records the modeled gotox target set > in insn_aux_data->jt. Later, check_indirect_jump() follows targets from > the runtime PTR_TO_INSN register's actual INSN_ARRAY map. > > This lets one gotox instruction observe different INSN_ARRAY maps on > different paths and accept a target outside the calling subprog. The > observed x86 JIT case can then enter another subprog without a matching > BPF call frame and crash when executed. > > Reject every target copied from the actual PTR_TO_INSN map if it is > outside the calling subprog. > > Fixes: 493d9e0d6083 ("bpf, x86: add support for indirect jumps") > Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> > --- > kernel/bpf/verifier.c | 19 +++++++++++++++++++ > 1 file changed, 19 insertions(+) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index eb46a81a8c51..05a996a5ecdd 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -17145,9 +17145,11 @@ static int indirect_jump_min_max_index(struct bpf_verifier_env *env, > static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *insn) > { > struct bpf_verifier_state *other_branch; > + struct bpf_subprog_info *subprog; > struct bpf_reg_state *dst_reg; > struct bpf_map *map; > u32 min_index, max_index; > + int subprog_start, subprog_end; > int err = 0; > int n; > int i; > @@ -17188,6 +17190,23 @@ static int check_indirect_jump(struct bpf_verifier_env *env, struct bpf_insn *in > return -EINVAL; > } > > + subprog = bpf_find_containing_subprog(env, env->insn_idx); > + if (verifier_bug_if(!subprog, env, > + "gotox insn %d is outside subprog bounds\n", > + env->insn_idx)) Can this actually happen? > + return -EFAULT; > + subprog_start = subprog->start; > + subprog_end = (subprog + 1)->start; > + > + for (i = 0; i < n; i++) { > + u32 target = env->gotox_tmp_buf->items[i]; > + > + if (target < subprog_start || target >= subprog_end) { > + verbose(env, "gotox target %u outside subprog\n", target); In the previous patch there was more info printed (at least, subprog boundaries looked ok, not 100% sure about map id). > + return -EINVAL; > + } > + } > + > for (i = 0; i < n - 1; i++) { > mark_indirect_target(env, env->gotox_tmp_buf->items[i]); > other_branch = push_stack(env, env->gotox_tmp_buf->items[i], > > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH bpf-next v3 2/2] selftests/bpf: Add cross-subprog gotox target coverage 2026-06-28 13:59 [PATCH bpf-next v3 0/2] bpf: Enforce gotox targets against subprog bounds Nuoqi Gui 2026-06-28 13:59 ` [PATCH bpf-next v3 1/2] " Nuoqi Gui @ 2026-06-28 13:59 ` Nuoqi Gui 2026-06-30 18:49 ` Anton Protopopov 2026-06-30 18:21 ` [PATCH bpf-next v3 0/2] bpf: Enforce gotox targets against subprog bounds Anton Protopopov 2 siblings, 1 reply; 8+ messages in thread From: Nuoqi Gui @ 2026-06-28 13:59 UTC (permalink / raw) To: bpf, John Fastabend, Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Anton Protopopov, Shuah Khan, linux-kselftest, linux-kernel, Nuoqi Gui Add a gotox regression test with two one-entry INSN_ARRAY maps. CFG can model a map whose target stays in the main subprog, while the verified path can load a different map whose target is the first instruction of another subprog. That second target is outside the subprog that contains this gotox instruction, so program load must be rejected with -EINVAL. Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> --- tools/testing/selftests/bpf/prog_tests/bpf_gotox.c | 73 ++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_gotox.c b/tools/testing/selftests/bpf/prog_tests/bpf_gotox.c index 73dc63882b7d..997724c61c8b 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_gotox.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_gotox.c @@ -255,6 +255,30 @@ static int create_jt_map(__u32 max_entries) key_size, value_size, max_entries, NULL); } +static int create_jt_map_with_target(__u32 target) +{ + struct bpf_insn_array_value val = { .orig_off = target }; + __u32 key = 0; + int map_fd; + + map_fd = create_jt_map(1); + if (!ASSERT_GE(map_fd, 0, "create_jt_map")) + return -1; + + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), + 0, "bpf_map_update_elem")) { + close(map_fd); + return -1; + } + + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) { + close(map_fd); + return -1; + } + + return map_fd; +} + static int prog_load(struct bpf_insn *insns, __u32 insn_cnt) { return bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT, NULL, "GPL", insns, insn_cnt, NULL); @@ -393,6 +417,52 @@ reject_offsets(struct bpf_insn *insns, __u32 insn_cnt, int off1, int off2, int o close(prog_fd); } +static void +check_cross_subprog_gotox_target(void) +{ + struct bpf_insn insns[] = { + /* main subprog [0,14) */ + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_CALL, 0, 12), + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 0), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_7, 0, 4), + BPF_LD_IMM64_RAW(BPF_REG_2, BPF_PSEUDO_MAP_VALUE, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, 0), + BPF_JMP_A(3), + BPF_LD_IMM64_RAW(BPF_REG_2, BPF_PSEUDO_MAP_VALUE, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, 0), + BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_2, 0, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + /* static subprog [14,16) */ + BPF_MOV64_IMM(BPF_REG_0, 42), + BPF_EXIT_INSN(), + }; + int good_fd, bad_fd, prog_fd; + + good_fd = create_jt_map_with_target(12); + if (!ASSERT_GE(good_fd, 0, "create_good_jt_map")) + return; + + bad_fd = create_jt_map_with_target(14); + if (!ASSERT_GE(bad_fd, 0, "create_bad_jt_map")) { + close(good_fd); + return; + } + + insns[4].imm = bad_fd; + insns[8].imm = good_fd; + + prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", + insns, ARRAY_SIZE(insns), NULL); + if (!ASSERT_EQ(prog_fd, -EINVAL, "cross_subprog_gotox_prog_load")) + close(prog_fd); + + close(bad_fd); + close(good_fd); +} + /* * Verify a bit more complex programs which include indirect jumps * and with jump tables loaded with a non-zero offset @@ -541,5 +611,8 @@ void test_bpf_gotox(void) if (test__start_subtest("check-ldimm64-off-gotox-llvm")) __subtest(skel, check_ldimm64_off_gotox_llvm); + if (test__start_subtest("check-cross-subprog-gotox-target")) + check_cross_subprog_gotox_target(); + bpf_gotox__destroy(skel); } -- 2.34.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v3 2/2] selftests/bpf: Add cross-subprog gotox target coverage 2026-06-28 13:59 ` [PATCH bpf-next v3 2/2] selftests/bpf: Add cross-subprog gotox target coverage Nuoqi Gui @ 2026-06-30 18:49 ` Anton Protopopov 0 siblings, 0 replies; 8+ messages in thread From: Anton Protopopov @ 2026-06-30 18:49 UTC (permalink / raw) To: Nuoqi Gui Cc: bpf, John Fastabend, Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Shuah Khan, linux-kselftest, linux-kernel On 26/06/28 09:59PM, Nuoqi Gui wrote: > Add a gotox regression test with two one-entry INSN_ARRAY maps. CFG can > model a map whose target stays in the main subprog, while the verified path > can load a different map whose target is the first instruction of another > subprog. > > That second target is outside the subprog that contains this gotox > instruction, so program load must be rejected with -EINVAL. > > Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> > --- > tools/testing/selftests/bpf/prog_tests/bpf_gotox.c | 73 ++++++++++++++++++++++ > 1 file changed, 73 insertions(+) > > diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_gotox.c b/tools/testing/selftests/bpf/prog_tests/bpf_gotox.c > index 73dc63882b7d..997724c61c8b 100644 > --- a/tools/testing/selftests/bpf/prog_tests/bpf_gotox.c > +++ b/tools/testing/selftests/bpf/prog_tests/bpf_gotox.c > @@ -255,6 +255,30 @@ static int create_jt_map(__u32 max_entries) > key_size, value_size, max_entries, NULL); > } > > +static int create_jt_map_with_target(__u32 target) > +{ > + struct bpf_insn_array_value val = { .orig_off = target }; > + __u32 key = 0; > + int map_fd; > + > + map_fd = create_jt_map(1); > + if (!ASSERT_GE(map_fd, 0, "create_jt_map")) > + return -1; > + > + if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), > + 0, "bpf_map_update_elem")) { > + close(map_fd); > + return -1; > + } > + > + if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze")) { > + close(map_fd); > + return -1; > + } > + > + return map_fd; > +} > + > static int prog_load(struct bpf_insn *insns, __u32 insn_cnt) > { > return bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT, NULL, "GPL", insns, insn_cnt, NULL); > @@ -393,6 +417,52 @@ reject_offsets(struct bpf_insn *insns, __u32 insn_cnt, int off1, int off2, int o > close(prog_fd); > } > > +static void > +check_cross_subprog_gotox_target(void) > +{ > + struct bpf_insn insns[] = { > + /* main subprog [0,14) */ > + BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), > + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_CALL, 0, 12), > + BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6, 0), > + BPF_JMP_IMM(BPF_JEQ, BPF_REG_7, 0, 4), > + BPF_LD_IMM64_RAW(BPF_REG_2, BPF_PSEUDO_MAP_VALUE, 0), > + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, 0), > + BPF_JMP_A(3), > + BPF_LD_IMM64_RAW(BPF_REG_2, BPF_PSEUDO_MAP_VALUE, 0), > + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, 0), > + BPF_RAW_INSN(BPF_JMP | BPF_JA | BPF_X, BPF_REG_2, 0, 0, 0), > + BPF_MOV64_IMM(BPF_REG_0, 1), > + BPF_EXIT_INSN(), > + > + /* static subprog [14,16) */ > + BPF_MOV64_IMM(BPF_REG_0, 42), > + BPF_EXIT_INSN(), > + }; > + int good_fd, bad_fd, prog_fd; > + > + good_fd = create_jt_map_with_target(12); > + if (!ASSERT_GE(good_fd, 0, "create_good_jt_map")) > + return; > + > + bad_fd = create_jt_map_with_target(14); > + if (!ASSERT_GE(bad_fd, 0, "create_bad_jt_map")) { > + close(good_fd); > + return; > + } > + > + insns[4].imm = bad_fd; > + insns[8].imm = good_fd; > + > + prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", > + insns, ARRAY_SIZE(insns), NULL); > + if (!ASSERT_EQ(prog_fd, -EINVAL, "cross_subprog_gotox_prog_load")) > + close(prog_fd); > + > + close(bad_fd); > + close(good_fd); > +} > + > /* > * Verify a bit more complex programs which include indirect jumps > * and with jump tables loaded with a non-zero offset > @@ -541,5 +611,8 @@ void test_bpf_gotox(void) > if (test__start_subtest("check-ldimm64-off-gotox-llvm")) > __subtest(skel, check_ldimm64_off_gotox_llvm); > > + if (test__start_subtest("check-cross-subprog-gotox-target")) > + check_cross_subprog_gotox_target(); > + > bpf_gotox__destroy(skel); > } LGTM now. Please add another selftests as was mentioned in the main patch. Also, v3 lost the ack from Yonghong Song for this patch. > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v3 0/2] bpf: Enforce gotox targets against subprog bounds 2026-06-28 13:59 [PATCH bpf-next v3 0/2] bpf: Enforce gotox targets against subprog bounds Nuoqi Gui 2026-06-28 13:59 ` [PATCH bpf-next v3 1/2] " Nuoqi Gui 2026-06-28 13:59 ` [PATCH bpf-next v3 2/2] selftests/bpf: Add cross-subprog gotox target coverage Nuoqi Gui @ 2026-06-30 18:21 ` Anton Protopopov 2 siblings, 0 replies; 8+ messages in thread From: Anton Protopopov @ 2026-06-30 18:21 UTC (permalink / raw) To: Nuoqi Gui Cc: bpf, John Fastabend, Kumar Kartikeya Dwivedi, Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Shuah Khan, linux-kselftest, linux-kernel On 26/06/28 09:59PM, Nuoqi Gui wrote: > For gotox, CFG construction models the indirect-jump target set in > insn_aux_data->jt, while do_check() later follows targets from the runtime > PTR_TO_INSN register's own INSN_ARRAY map. If the same gotox can be > reached with PTR_TO_INSN values from different maps, do_check() can accept > a target outside the calling subprog. Can we use some human-readable description here? Please just explain that maps considered during the config stage must be a super-set of maps checked runtime. > The observed x86 JIT case can then enter another subprog without a matching > BPF call frame and crash when the program is run. Sorry, but why the x86 is still here? > > Fix this by rejecting gotox map targets outside the current gotox subprog. > Add a regression test covering the two-map cross-subprog case. > > v1 -> v2: > - Validate gotox runtime targets against the current subprog bounds instead > of scanning the CFG jump table. > - Fix the selftest expected error from -EACCES to -EINVAL. > > v2 -> v3: > - Drop the Validation section from the cover letter. > - Clarify that the crash was observed through the x86 JIT path while the > verifier invariant is generic. > - Simplify the cover letter and commit message. > - Remove the unused skel argument from the raw-insn selftest. > - Move the raw-insn selftest to the end of test_bpf_gotox(). > > v1: > https://lore.kernel.org/bpf/20260609-f01-03-gotox-bpf-next-v1-0-b441d63a1559@mails.tsinghua.edu.cn/ > > v2: > https://lore.kernel.org/bpf/20260613-f01-03-gotox-bpf-next-v2-send-v2-0-7c883b43f3c3@mails.tsinghua.edu.cn/ > > Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> > --- > Nuoqi Gui (2): > bpf: Enforce gotox targets against subprog bounds > selftests/bpf: Add cross-subprog gotox target coverage > > kernel/bpf/verifier.c | 19 ++++++ > tools/testing/selftests/bpf/prog_tests/bpf_gotox.c | 73 ++++++++++++++++++++++ > 2 files changed, 92 insertions(+) > --- > base-commit: 7bfb93e3475be9de894f1cecd3a727d3e1649b03 > change-id: 20260628-f01-03-gotox-bpf-next-1a7af91d2c82 > > Best regards, > -- > Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> > ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-06-30 18:39 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-28 13:59 [PATCH bpf-next v3 0/2] bpf: Enforce gotox targets against subprog bounds Nuoqi Gui 2026-06-28 13:59 ` [PATCH bpf-next v3 1/2] " Nuoqi Gui 2026-06-28 14:24 ` sashiko-bot 2026-06-30 18:30 ` Anton Protopopov 2026-06-30 18:42 ` Anton Protopopov 2026-06-28 13:59 ` [PATCH bpf-next v3 2/2] selftests/bpf: Add cross-subprog gotox target coverage Nuoqi Gui 2026-06-30 18:49 ` Anton Protopopov 2026-06-30 18:21 ` [PATCH bpf-next v3 0/2] bpf: Enforce gotox targets against subprog bounds Anton Protopopov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox