* [PATCH bpf 0/2] bpf: Fix stack argument precision propagation
@ 2026-10-27 6:13 Xu Yunxiang
2026-10-27 6:13 ` [PATCH bpf 1/2] bpf: Propagate scalar precision through stack arguments Xu Yunxiang
2026-10-27 6:13 ` [PATCH bpf 2/2] selftests/bpf: Exercise stack argument precision on state reuse Xu Yunxiang
0 siblings, 2 replies; 4+ messages in thread
From: Xu Yunxiang @ 2026-10-27 6:13 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Yonghong Song
This series fixes two omissions in scalar precision tracking for BPF
stack arguments.
State comparison already uses precision marks on outgoing argument slots,
but state reuse does not propagate their dependencies to the current
path's ancestors. Earlier checkpoints can therefore fail to distinguish
scalar argument values later used as memory offsets. The conservative
all-scalars-precise fallback also omits argument slots.
Patch 1 includes stack argument slots in state-reuse precision propagation
and in the conservative fallback, including slots owned by caller frames.
Patch 2 adds nine load-only tests covering the sixth and seventh arguments,
caller and nested frames, acceptance and rejection controls, and fallback
log checks. The tests construct instructions and BTF directly, so they do
not require compiler support for emitting stack arguments.
Built the kernel, modules and complete test_progs with BPF_STRICT_BUILD=1.
On x86-64 KVM, all nine new tests and 246 existing subtests passed, covering
verifier_precision, verifier_spill_fill, verifier_subprog_precision,
verifier_scalar_ids and iters. The unmodified base fails six of the new
tests as expected. Three existing compiler-dependent stack-argument groups
were skipped with Clang 20; all new tests ran.
AI assistance was used to prepare the patches, tests and descriptions.
Xu Yunxiang (2):
bpf: Propagate scalar precision through stack arguments
selftests/bpf: Exercise stack argument precision on state reuse
kernel/bpf/backtrack.c | 11 +
kernel/bpf/states.c | 14 +
.../bpf/prog_tests/stack_arg_precision_reuse.c | 300 +++++++++++++++++++++
3 files changed, 325 insertions(+)
base-commit: 195a3519ff27bff5575e05b5086e38ee92715776
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH bpf 1/2] bpf: Propagate scalar precision through stack arguments
2026-10-27 6:13 [PATCH bpf 0/2] bpf: Fix stack argument precision propagation Xu Yunxiang
@ 2026-10-27 6:13 ` Xu Yunxiang
2026-10-27 6:13 ` [PATCH bpf 2/2] selftests/bpf: Exercise stack argument precision on state reuse Xu Yunxiang
1 sibling, 0 replies; 4+ messages in thread
From: Xu Yunxiang @ 2026-10-27 6:13 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Yonghong Song
Outgoing stack arguments retain scalar verifier state separately from
registers and ordinary spills. State comparison uses their precision
marks, but propagate_precision() does not carry those dependencies from
a reused state to the current path's ancestors. A path can consequently
reuse a state without making earlier checkpoints distinguish scalar
values that will later be used as memory offsets.
Seed the existing backtracking batch with precise scalar argument slots
from every frame. Incoming arguments live in the caller's outgoing
slots, so include caller frames as well as the current frame.
Also cover scalar argument slots in bpf_mark_all_scalars_precise(), so
conservative fallback preserves the same invariant. Keep its existing
ancestor-only traversal and leave pointer and uninitialized slots alone.
AI assistance was used in preparing this patch.
Fixes: 0f6bd5e7a804 ("bpf: Support stack arguments for bpf functions")
Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
---
kernel/bpf/backtrack.c | 11 +++++++++++
kernel/bpf/states.c | 14 ++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c
index 43d5740034c0fdd53daf6ef572a56dce2eb78083..2d6535f271a67226ae972305033b0f7f19f58520 100644
--- a/kernel/bpf/backtrack.c
+++ b/kernel/bpf/backtrack.c
@@ -746,6 +746,17 @@ void bpf_mark_all_scalars_precise(struct bpf_verifier_env *env,
i, -(j + 1) * 8);
}
}
+ for (j = 0; j < func->out_stack_arg_cnt; j++) {
+ reg = &func->stack_arg_regs[j];
+ if (reg->type != SCALAR_VALUE || reg->precise)
+ continue;
+ reg->precise = true;
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ verbose(env,
+ "force_precise: frame%d: forcing r11%d to be precise\n",
+ i, -(j + 1) * BPF_REG_SIZE);
+ }
+ }
}
}
}
diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c
index d7df6632a5c47a422e9de50edb1fd753378a755f..37b96296e8835a61903cfffeece6a087771cb82c 100644
--- a/kernel/bpf/states.c
+++ b/kernel/bpf/states.c
@@ -1116,6 +1116,20 @@ static int propagate_precision(struct bpf_verifier_env *env,
bpf_bt_set_frame_slot(&env->bt, fr, i);
first = false;
}
+ for (i = 0; i < state->out_stack_arg_cnt; i++) {
+ state_reg = &state->stack_arg_regs[i];
+ if (state_reg->type != SCALAR_VALUE || !state_reg->precise)
+ continue;
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ if (first)
+ verbose(env, "frame %d: propagating r11%d",
+ fr, -(i + 1) * BPF_REG_SIZE);
+ else
+ verbose(env, ",r11%d", -(i + 1) * BPF_REG_SIZE);
+ }
+ bt_set_frame_stack_arg_slot(&env->bt, fr, i);
+ first = false;
+ }
if (!first && (env->log.level & BPF_LOG_LEVEL2))
verbose(env, "\n");
}
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH bpf 2/2] selftests/bpf: Exercise stack argument precision on state reuse
2026-10-27 6:13 [PATCH bpf 0/2] bpf: Fix stack argument precision propagation Xu Yunxiang
2026-10-27 6:13 ` [PATCH bpf 1/2] bpf: Propagate scalar precision through stack arguments Xu Yunxiang
@ 2026-10-27 6:13 ` Xu Yunxiang
2026-10-27 6:27 ` sashiko-bot
1 sibling, 1 reply; 4+ messages in thread
From: Xu Yunxiang @ 2026-10-27 6:13 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Yonghong Song
Add load-only verifier regressions for state reuse with the sixth and
seventh arguments, including joins inside callees and nested frames.
An in-bounds offset and a discarded offset provide acceptance controls;
an ordinary register argument provides a rejection control.
Exercise conservative precision fallback separately with bounded
pointer subtraction, and require log evidence that both argument slots
are marked precise, including when they reside in the caller frame.
Build instructions and BTF directly so these tests run even when Clang
does not yet support emitting stack arguments.
AI assistance was used in preparing this patch.
Signed-off-by: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
---
.../prog_tests/stack_arg_precision_reuse.c | 300 ++++++++++++++++++
1 file changed, 300 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c
diff --git a/tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c b/tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c
new file mode 100644
index 0000000000000000000000000000000000000000..4497d959247d75bb38668b6c0358d756908183ea
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <bpf/btf.h>
+
+#define BPF_REG_PARAMS 11
+
+/* Construct BTF and instructions directly so these tests also run with
+ * compilers which cannot emit stack arguments yet.
+ */
+struct test_program {
+ struct bpf_insn insns[128];
+ struct bpf_func_info funcs[3];
+ struct btf *btf;
+ int insn_cnt;
+ int func_cnt;
+};
+
+static char log_buf[1024 * 1024];
+
+static int emit(struct test_program *p, struct bpf_insn insn)
+{
+ int idx = p->insn_cnt++;
+
+ p->insns[idx] = insn;
+ return idx;
+}
+
+static void jump_to(struct test_program *p, int from, int to)
+{
+ p->insns[from].off = to - from - 1;
+}
+
+static bool add_func(struct test_program *p, int nargs)
+{
+ char name[16];
+ int proto, id, i;
+
+ proto = btf__add_func_proto(p->btf, 1);
+ if (!ASSERT_GT(proto, 0, "func_proto"))
+ return false;
+ for (i = 0; i < nargs; i++) {
+ snprintf(name, sizeof(name), "arg%d", i + 1);
+ id = btf__add_func_param(p->btf, name, i == 0 ? 2 : 1);
+ if (!ASSERT_OK(id, "func_param"))
+ return false;
+ }
+ snprintf(name, sizeof(name), "func%d", p->func_cnt);
+ id = btf__add_func(p->btf, name,
+ p->func_cnt ? BTF_FUNC_STATIC : BTF_FUNC_GLOBAL, proto);
+ if (!ASSERT_GT(id, 0, "func"))
+ return false;
+ p->funcs[p->func_cnt++] = (struct bpf_func_info) {
+ .insn_off = p->insn_cnt,
+ .type_id = id,
+ };
+ return true;
+}
+
+static bool init_program(struct test_program *p)
+{
+ p->btf = btf__new_empty();
+ if (!ASSERT_OK_PTR(p->btf, "btf_new"))
+ return false;
+ if (!ASSERT_EQ(btf__add_int(p->btf, "u64", 8, 0), 1, "btf_int") ||
+ !ASSERT_EQ(btf__add_ptr(p->btf, 0), 2, "btf_ptr"))
+ return false;
+ return add_func(p, 1);
+}
+
+static void lookup_map(struct test_program *p, int map_fd)
+{
+ struct bpf_insn insns[] = {
+ BPF_ST_MEM(BPF_W, BPF_REG_10, -8, 0),
+ BPF_LD_MAP_FD(BPF_REG_1, map_fd),
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+ BPF_EXIT_INSN(),
+ BPF_MOV64_REG(BPF_REG_7, BPF_REG_0),
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(insns); i++)
+ emit(p, insns[i]);
+}
+
+struct reuse_test {
+ const char *name;
+ int slot;
+ int offset;
+ bool discard;
+ bool callee_join;
+ bool nested;
+};
+
+static bool build_reuse(struct test_program *p, int map_fd,
+ const struct reuse_test *test)
+{
+ int branch, path1, path2, path3, call, i;
+ int off = -(test->slot + 1) * 8;
+
+ if (test->nested) {
+ emit(p, BPF_CALL_REL(1));
+ emit(p, BPF_EXIT_INSN());
+ if (!add_func(p, 1))
+ return false;
+ }
+ lookup_map(p, map_fd);
+ emit(p, BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32));
+ /* Keep the unused earlier argument initialized when testing arg7. */
+ for (i = 0; i < test->slot; i++)
+ emit(p, BPF_ST_MEM(BPF_DW, BPF_REG_PARAMS, -(i + 1) * 8, 0));
+ branch = emit(p, BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0));
+ emit(p, BPF_MOV64_IMM(BPF_REG_6, 0));
+ if (test->slot >= 0)
+ emit(p, BPF_ST_MEM(BPF_DW, BPF_REG_PARAMS, off, 0));
+ if (test->callee_join)
+ emit(p, BPF_MOV64_IMM(BPF_REG_8, 1));
+ path1 = emit(p, BPF_JMP_A(0));
+ jump_to(p, branch, p->insn_cnt);
+ if (test->callee_join)
+ emit(p, BPF_MOV64_IMM(BPF_REG_8, 0));
+ branch = emit(p, BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 1, 0));
+ emit(p, BPF_MOV64_IMM(BPF_REG_6, 0));
+ path2 = emit(p, BPF_JMP_A(0));
+ jump_to(p, branch, p->insn_cnt);
+ emit(p, BPF_MOV64_IMM(BPF_REG_6, test->offset));
+ path3 = emit(p, BPF_JMP_A(0));
+ /* M: paths 2 and 3 meet with their offsets still in r6. Path 2
+ * must acquire precision here when it reuses path 1's state at P.
+ */
+ jump_to(p, path2, p->insn_cnt);
+ jump_to(p, path3, p->insn_cnt);
+ if (test->slot >= 0) {
+ emit(p, BPF_STX_MEM(BPF_DW, BPF_REG_PARAMS, BPF_REG_6, off));
+ emit(p, BPF_MOV64_IMM(BPF_REG_6, 0));
+ }
+ branch = emit(p, BPF_JMP_A(0));
+ /* P: the precise offset is now in the outgoing argument slot. */
+ jump_to(p, path1, p->insn_cnt);
+ jump_to(p, branch, p->insn_cnt);
+ emit(p, BPF_MOV64_REG(BPF_REG_1, BPF_REG_7));
+ for (i = 2; i <= 5; i++) {
+ if (i == 2 && (test->slot < 0 || test->callee_join))
+ emit(p, BPF_MOV64_REG(i, test->slot < 0 ? BPF_REG_6 : BPF_REG_8));
+ else
+ emit(p, BPF_MOV64_IMM(i, 0));
+ }
+ call = emit(p, BPF_CALL_REL(0));
+ emit(p, BPF_EXIT_INSN());
+ p->insns[call].imm = p->insn_cnt - call - 1;
+ if (!add_func(p, test->slot < 0 ? 2 : 6 + test->slot))
+ return false;
+ /* Different precise r2 values prevent reuse before the callee.
+ * They converge here while the offset is still in the caller's slot.
+ */
+ if (test->callee_join) {
+ emit(p, BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 0, 2));
+ emit(p, BPF_MOV64_IMM(BPF_REG_2, 0));
+ emit(p, BPF_JMP_A(0));
+ }
+ if (test->slot < 0)
+ emit(p, BPF_MOV64_REG(BPF_REG_6, BPF_REG_2));
+ else
+ emit(p, BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_PARAMS, -off));
+ if (test->discard)
+ emit(p, BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0));
+ emit(p, BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_6));
+ emit(p, BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 0));
+ emit(p, BPF_EXIT_INSN());
+ return true;
+}
+
+static bool build_fallback(struct test_program *p, int map_fd, bool callee)
+{
+ lookup_map(p, map_fd);
+ emit(p, BPF_EMIT_CALL(BPF_FUNC_get_prandom_u32));
+ emit(p, BPF_STX_MEM(BPF_DW, BPF_REG_PARAMS, BPF_REG_0, -8));
+ emit(p, BPF_STX_MEM(BPF_DW, BPF_REG_PARAMS, BPF_REG_0, -16));
+ emit(p, BPF_JMP_A(0));
+ if (callee) {
+ emit(p, BPF_MOV64_REG(BPF_REG_1, BPF_REG_7));
+ emit(p, BPF_MOV64_IMM(BPF_REG_2, 0));
+ emit(p, BPF_MOV64_IMM(BPF_REG_3, 0));
+ emit(p, BPF_MOV64_IMM(BPF_REG_4, 0));
+ emit(p, BPF_MOV64_IMM(BPF_REG_5, 0));
+ emit(p, BPF_CALL_REL(1));
+ emit(p, BPF_EXIT_INSN());
+ if (!add_func(p, 7))
+ return false;
+ emit(p, BPF_MOV64_REG(BPF_REG_7, BPF_REG_1));
+ }
+ /* Subtraction turns r1 into a scalar. Precision backtracking across
+ * the store through its old pointer value needs conservative fallback.
+ * The resulting offset is bounded, so this program is always safe.
+ */
+ emit(p, BPF_MOV64_REG(BPF_REG_1, BPF_REG_7));
+ emit(p, BPF_ST_MEM(BPF_W, BPF_REG_1, 0, 0));
+ emit(p, BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_7));
+ emit(p, BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 7));
+ emit(p, BPF_ALU64_REG(BPF_ADD, BPF_REG_7, BPF_REG_1));
+ emit(p, BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_7, 0));
+ if (!callee) {
+ /* Consume the outgoing slots after exercising fallback. */
+ emit(p, BPF_MOV64_REG(BPF_REG_1, BPF_REG_7));
+ emit(p, BPF_MOV64_IMM(BPF_REG_2, 0));
+ emit(p, BPF_MOV64_IMM(BPF_REG_3, 0));
+ emit(p, BPF_MOV64_IMM(BPF_REG_4, 0));
+ emit(p, BPF_MOV64_IMM(BPF_REG_5, 0));
+ emit(p, BPF_CALL_REL(1));
+ emit(p, BPF_EXIT_INSN());
+ if (!add_func(p, 7))
+ return false;
+ emit(p, BPF_MOV64_IMM(BPF_REG_0, 0));
+ }
+ emit(p, BPF_EXIT_INSN());
+ return true;
+}
+
+static void check_program(struct test_program *p, bool reject, bool fallback)
+{
+ LIBBPF_OPTS(bpf_prog_load_opts, opts,
+ .prog_flags = BPF_F_TEST_STATE_FREQ,
+ .log_buf = log_buf,
+ .log_size = sizeof(log_buf),
+ .log_level = 2,
+ .func_info = p->funcs,
+ .func_info_cnt = p->func_cnt,
+ .func_info_rec_size = sizeof(p->funcs[0]),
+ );
+ int fd;
+
+ if (!ASSERT_OK(btf__load_into_kernel(p->btf), "load_btf"))
+ return;
+ opts.prog_btf_fd = btf__fd(p->btf);
+ log_buf[0] = '\0';
+ fd = bpf_prog_load(BPF_PROG_TYPE_SCHED_CLS, "stack_precision", "GPL",
+ p->insns, p->insn_cnt, &opts);
+ if (env.verbosity > VERBOSE_NONE)
+ printf("%s\n", log_buf);
+ if (reject) {
+ ASSERT_LT(fd, 0, "reject_unsafe_offset");
+ ASSERT_HAS_SUBSTR(log_buf, "invalid access to map value", "bounds_error");
+ } else {
+ ASSERT_GE(fd, 0, "accept_safe_program");
+ }
+ if (fallback) {
+ ASSERT_HAS_SUBSTR(log_buf, "falling back to forcing all scalars precise",
+ "fallback_reached");
+ ASSERT_HAS_SUBSTR(log_buf, "force_precise: frame0: forcing r11-8 to be precise",
+ "fallback_arg6");
+ ASSERT_HAS_SUBSTR(log_buf, "force_precise: frame0: forcing r11-16 to be precise",
+ "fallback_arg7");
+ }
+ if (fd >= 0)
+ close(fd);
+}
+
+void test_stack_arg_precision_reuse(void)
+{
+ const struct reuse_test tests[] = {
+ { .name = "arg6", .slot = 0, .offset = 32 },
+ { .name = "arg7", .slot = 1, .offset = 32 },
+ { .name = "caller_frame", .slot = 0, .offset = 32, .callee_join = true },
+ { .name = "nested_caller_frame", .slot = 1, .offset = 32,
+ .callee_join = true, .nested = true },
+ { .name = "in_bounds", .slot = 1, .offset = 8 },
+ { .name = "discarded", .slot = 0, .offset = 32, .discard = true },
+ { .name = "register_control", .slot = -1, .offset = 32 },
+ };
+ struct test_program p;
+ int map_fd, i;
+
+ /* Stack argument JIT support currently exists on x86-64 and arm64. */
+#if !defined(__x86_64__) && !defined(__aarch64__)
+ test__skip();
+ return;
+#endif
+ map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, 4, 16, 1, NULL);
+ if (!ASSERT_GE(map_fd, 0, "map_create"))
+ return;
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ if (!test__start_subtest(tests[i].name))
+ continue;
+ memset(&p, 0, sizeof(p));
+ if (init_program(&p) && build_reuse(&p, map_fd, &tests[i]))
+ check_program(&p, tests[i].offset > 8 && !tests[i].discard, false);
+ btf__free(p.btf);
+ }
+ for (i = 0; i < 2; i++) {
+ if (!test__start_subtest(i ? "fallback_caller_frame" : "fallback"))
+ continue;
+ memset(&p, 0, sizeof(p));
+ if (init_program(&p) && build_fallback(&p, map_fd, i))
+ check_program(&p, false, true);
+ btf__free(p.btf);
+ }
+ close(map_fd);
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH bpf 2/2] selftests/bpf: Exercise stack argument precision on state reuse
2026-10-27 6:13 ` [PATCH bpf 2/2] selftests/bpf: Exercise stack argument precision on state reuse Xu Yunxiang
@ 2026-10-27 6:27 ` sashiko-bot
0 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-10-27 6:27 UTC (permalink / raw)
To: Xu Yunxiang, bpf
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] selftests/bpf: multi-line comments violate BPF comment style
- [Low] selftests/bpf: test_stack_arg_precision_reuse() skips supported RV64
--
commit 46da3caeb85de86ad2e22a94e1a61ab12b6a1cfc
Author: Xu Yunxiang <xyx2021@mail.ustc.edu.cn>
selftests/bpf: Exercise stack argument precision on state reuse
This commit adds load-only BPF verifier selftests in
tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c to
exercise stack argument precision tracking across state reuse and
conservative precision fallback. It constructs BPF instructions and BTF
directly so the tests can run even when the compiler cannot emit stack
arguments.
> diff --git a/tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c b/tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c
> new file mode 100644
> index 0000000000000..4497d959247d7
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c
> @@ -0,0 +1,300 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <test_progs.h>
> +#include <bpf/btf.h>
> +
> +#define BPF_REG_PARAMS 11
> +
> +/* Construct BTF and instructions directly so these tests also run with
> + * compilers which cannot emit stack arguments yet.
> + */
> +struct test_program {
[ ... ]
> +static bool build_reuse(struct test_program *p, int map_fd,
> + const struct reuse_test *test)
> +{
[ ... ]
> + /* M: paths 2 and 3 meet with their offsets still in r6. Path 2
> + * must acquire precision here when it reuses path 1's state at P.
> + */
> + jump_to(p, path2, p->insn_cnt);
[ ... ]
> + /* Different precise r2 values prevent reuse before the callee.
> + * They converge here while the offset is still in the caller's slot.
> + */
> + if (test->callee_join) {
[ ... ]
> +static bool build_fallback(struct test_program *p, int map_fd, bool callee)
> +{
[ ... ]
> + /* Subtraction turns r1 into a scalar. Precision backtracking across
> + * the store through its old pointer value needs conservative fallback.
> + * The resulting offset is bounded, so this program is always safe.
> + */
> + emit(p, BPF_MOV64_REG(BPF_REG_1, BPF_REG_7));
[Severity: Low]
This isn't a bug, but in
tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c, the four
multi-line comments above struct test_program, inside build_reuse(), and
inside build_fallback() place text on the opening /* line. Could these use
the standard BPF multi-line comment style with /* on its own line?
[ ... ]
> +void test_stack_arg_precision_reuse(void)
> +{
> + const struct reuse_test tests[] = {
> + { .name = "arg6", .slot = 0, .offset = 32 },
> + { .name = "arg7", .slot = 1, .offset = 32 },
> + { .name = "caller_frame", .slot = 0, .offset = 32, .callee_join = true },
> + { .name = "nested_caller_frame", .slot = 1, .offset = 32,
> + .callee_join = true, .nested = true },
> + { .name = "in_bounds", .slot = 1, .offset = 8 },
> + { .name = "discarded", .slot = 0, .offset = 32, .discard = true },
> + { .name = "register_control", .slot = -1, .offset = 32 },
> + };
> + struct test_program p;
> + int map_fd, i;
> +
> + /* Stack argument JIT support currently exists on x86-64 and arm64. */
> +#if !defined(__x86_64__) && !defined(__aarch64__)
> + test__skip();
> + return;
> +#endif
[Severity: Low]
In test_stack_arg_precision_reuse() in
tools/testing/selftests/bpf/prog_tests/stack_arg_precision_reuse.c, when
compiled and run on 64-bit RISC-V, this guard unconditionally calls
test__skip().
However, arch/riscv/net/bpf_jit_comp64.c implements
bpf_jit_supports_stack_args() returning true, and other stack argument
selftests such as tools/testing/selftests/bpf/progs/stack_arg.c enable stack
argument testing on 64-bit RISC-V.
Should this condition and its comment also include 64-bit RISC-V (for
example, defined(__riscv) && __riscv_xlen == 64) so these verifier tests are
not skipped on RV64?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261027061303.1754535-1-xyx2021@mail.ustc.edu.cn?part=2
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-27 6:27 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 6:13 [PATCH bpf 0/2] bpf: Fix stack argument precision propagation Xu Yunxiang
2026-10-27 6:13 ` [PATCH bpf 1/2] bpf: Propagate scalar precision through stack arguments Xu Yunxiang
2026-10-27 6:13 ` [PATCH bpf 2/2] selftests/bpf: Exercise stack argument precision on state reuse Xu Yunxiang
2026-10-27 6:27 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.