From 0ceb2f21d3e02b310ed246a210b6cecea24e19b2 Mon Sep 17 00:00:00 2001 From: KaFai Wan Date: Sun, 18 Oct 2026 08:53:45 +0800 Subject: [PATCH 1/4] bpf: Determine freplace staticness from BTF linkage bpf_check_attach_target() rejects a freplace whose target is a static function, using the subprog "unreliable" flag as a proxy for "static". That flag is raised whenever a subprogram's BTF prototype disagrees with the registers at its call site. `147400e9c27c ("bpf: Compute parameters liveness in bpf_compute_live_registers()")` made that work for static callees: a static function with an unused argument no longer matches its BTF prototype at the call site and is marked unreliable. Since LLVM commit `29b36a16f111 ("[BPF] Emit ABI-accurate BTF prototypes for DW_CC_nocall")` in 23.1 [1] that premise is gone: BTFDebug may now drop arguments removed by the optimizer, so for __noinline static int subprog(struct __sk_buff *ctx) { return bpf_ktime_get_ns(); } compiled with clang 23 the BTF describes subprog() as [75] FUNC_PROTO '(anon)' ret_type_id=2 vlen=0 [76] FUNC 'subprog' type_id=75 linkage=static There is then no argument left to compare, the subprog stays reliable, and the check in bpf_check_attach_target() is skipped. The freplace is then rejected by the later linkage check instead, reporting "subprog() is not a global function", and the selftest "exceptions" fails. Determine staticness from the BTF linkage instead, and keep the unreliable check for global functions whose BTF does not match their call site. There is no functional change: freplacing a static function was rejected before and is still rejected with the same -EINVAL. Selftest "exceptions" is updated to match. [1] https://github.com/llvm/llvm-project/pull/198426 Fixes: 147400e9c27c ("bpf: Compute parameters liveness in bpf_compute_live_registers()") Signed-off-by: KaFai Wan Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261018005348.2195130-2-kafai.wan@linux.dev --- kernel/bpf/verifier.c | 3 ++- tools/testing/selftests/bpf/prog_tests/exceptions.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 63fff641d985..74e5ac248a10 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -22301,7 +22301,8 @@ int bpf_check_attach_target(struct bpf_verifier_log *log, } conservative = aux->func_info_aux[subprog].unreliable; if (prog_extension) { - if (conservative) { + if (aux->func_info_aux[subprog].linkage != BTF_FUNC_GLOBAL || + conservative) { bpf_log(log, "Cannot replace static functions\n"); return -EINVAL; diff --git a/tools/testing/selftests/bpf/prog_tests/exceptions.c b/tools/testing/selftests/bpf/prog_tests/exceptions.c index cea01d638f6f..1bd15c4c125b 100644 --- a/tools/testing/selftests/bpf/prog_tests/exceptions.c +++ b/tools/testing/selftests/bpf/prog_tests/exceptions.c @@ -333,7 +333,7 @@ static void test_exceptions_success(void) bpf_program__fd(skel->progs.exception_throw_subprog), "throwing_subprog"), "set_attach_target")) goto done; - }), "throwing_subprog() is not a global function", 0); + }), "Cannot replace static functions", 0); if (test__start_subtest("non-throwing extension -> non-throwing subprog")) /* non-throwing extension -> non-throwing global subprog : OK */ From 8d637d4c62c1880062dbb098f67b804e2eff85aa Mon Sep 17 00:00:00 2001 From: KaFai Wan Date: Sun, 18 Oct 2026 08:53:46 +0800 Subject: [PATCH 2/4] selftests/bpf: Fix stack_arg precision backtracking log expectation Commit 147400e9c27c ("bpf: Compute parameters liveness in bpf_compute_live_registers()") stopped assuming that a call to a static subprogram reads r1..r5 and now uses the callee's entry live-in. R1-R5 are not live at these call insns, so __clean_func_state() clears them from the caller state and the backtracked parent state prints as: mark_precise: frame0: parent state regs= stack=: R10=fp0 Update both expectations accordingly. Fixes: 147400e9c27c ("bpf: Compute parameters liveness in bpf_compute_live_registers()") Signed-off-by: KaFai Wan Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261018005348.2195130-3-kafai.wan@linux.dev --- tools/testing/selftests/bpf/progs/stack_arg_precision.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/stack_arg_precision.c b/tools/testing/selftests/bpf/progs/stack_arg_precision.c index 7baa9c1c8e31..c9564776d733 100644 --- a/tools/testing/selftests/bpf/progs/stack_arg_precision.c +++ b/tools/testing/selftests/bpf/progs/stack_arg_precision.c @@ -74,7 +74,7 @@ __msg("mark_precise: frame1: parent state regs= stack=: frame1: R10=fp0") __msg("mark_precise: frame0: parent state regs= stack=: R10=fp0") __msg("mark_precise: frame1: last_idx 11 first_idx 11 subseq_idx 13") __msg("mark_precise: frame1: regs= stack= before 11: (85) call pc+1") -__msg("mark_precise: frame0: parent state regs= stack=: R1=1 R2=2 R3=3 R4=4 R5=5 R10=fp0") +__msg("mark_precise: frame0: parent state regs= stack=: R10=fp0") __msg("mark_precise: frame0: last_idx 9 first_idx 7 subseq_idx 11") __msg("mark_precise: frame0: regs= stack= before 9: (05) goto pc+1") __msg("mark_precise: frame0: regs= stack= before 8: (7a) *(u64 *)(r11 -8) = 4") @@ -95,7 +95,7 @@ __msg("mark_precise: frame1: parent state regs= stack=: frame1: R10=fp0") __msg("mark_precise: frame0: parent state regs= stack=: R10=fp0") __msg("mark_precise: frame1: last_idx 11 first_idx 11 subseq_idx 13 ") __msg("mark_precise: frame1: regs= stack= before 11: (85) call pc+1") -__msg("mark_precise: frame0: parent state regs= stack=: R1=1 R2=2 R3=3 R4=4 R5=5 R10=fp0") +__msg("mark_precise: frame0: parent state regs= stack=: R10=fp0") __msg("mark_precise: frame0: last_idx 10 first_idx 10 subseq_idx 11 ") __msg("mark_precise: frame0: regs= stack= before 10: (7a) *(u64 *)(r11 -8) = 6") __naked void stack_arg_precision_bpf2bpf(void) From 8342d47ad791d70a42ab9cf262bd477fb41fdff2 Mon Sep 17 00:00:00 2001 From: KaFai Wan Date: Sun, 18 Oct 2026 08:53:47 +0800 Subject: [PATCH 3/4] selftests/bpf: Fix stack arg pruning test JOIN log expectation Commit f414d20879ae ("bpf: Save DFS postorder numbers for program instructions") changed bpf_compute_postorder() to use a real DFS, which changes the postorder of this test's instructions and thus the insn edge the arg track join at insn 10 is reported on: arg JOIN insn 8 -> 10 r1: _ + fp0-8 => fp0-8|fp0+0 arg JOIN insn 8 -> 10 sa0: _ + fp0-8 => fp0-8|fp0+0 Update the r1 and sa0 expectations to the new output. Fixes: f414d20879ae ("bpf: Save DFS postorder numbers for program instructions") Signed-off-by: KaFai Wan Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261018005348.2195130-4-kafai.wan@linux.dev --- tools/testing/selftests/bpf/progs/verifier_stack_arg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c index f6a4a6aa0e61..b9f799f6e405 100644 --- a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c +++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c @@ -117,8 +117,8 @@ SEC("tc") __description("stack_arg: pruning with different stack arg types") __failure __log_level(2) __flag(BPF_F_TEST_STATE_FREQ) -__msg("arg JOIN insn 9 -> 10 r1: fp0-8 + _ => fp0-8|fp0+0") -__msg("arg JOIN insn 9 -> 10 sa0: fp0-8 + _ => fp0-8|fp0+0") +__msg("arg JOIN insn 8 -> 10 r1: _ + fp0-8 => fp0-8|fp0+0") +__msg("arg JOIN insn 8 -> 10 sa0: _ + fp0-8 => fp0-8|fp0+0") __msg("R{{[0-9]}} invalid mem access 'scalar'") __naked void stack_arg_pruning_type_mismatch(void) { From ce42271dc517baa880352d76548d301850e7385b Mon Sep 17 00:00:00 2001 From: KaFai Wan Date: Sun, 18 Oct 2026 08:53:48 +0800 Subject: [PATCH 4/4] selftests/bpf: Use _exit() in uprobe_multi_test clone-ed child child_func() terminates with exit(), but spawn_child_flag() also starts it via clone(CLONE_VM), which shares the parent's address space. That exit() then runs atexit handlers and static destructors against globals the parent is still using. LLVM commit `3c3ed59c6a2a ("[llvm] Add format check for MCSubtargetFeatures")` [1], included in the LLVM 23.1 releases, added Target::isValidFeatureListFormat() with a function-local static holding the compiled regex: static const llvm::Regex pattern("^([+-][^,]+)(,[+-][^,]+)*,?$"); return pattern.match(Features); The child exit destroys that static while its guard variable still marks it as initialized, so the regex is never rebuilt in the parent. From that point on, get_jited_program_text() gets NULL from LLVMCreateDisasmCPUFeatures(), so every jited program text check in the tests running after filter_clone_vm fails, e.g. "verifier_jit_convergence", "verifier_private_stack", "verifier_tailcall_jit" and "verifier_jit_inline". Use _exit(), which runs neither atexit handlers nor static destructors, as uprobe_syscall.c already does for its own CLONE_VM child. [1] https://github.com/llvm/llvm-project/pull/180943 Fixes: d2520bdb1932f ("selftests/bpf: Add uprobe multi pid filter test for clone-ed processes") Signed-off-by: KaFai Wan Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261018005348.2195130-5-kafai.wan@linux.dev --- tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c index f0baf5738b75..7fbcfddc3cc3 100644 --- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c @@ -94,14 +94,14 @@ static int child_func(void *arg) /* wait for parent's kick */ err = read(child->go[0], &c, 1); if (err != 1) - exit(err); + _exit(err); uprobe_multi_func_1(); uprobe_multi_func_2(); uprobe_multi_func_3(); usdt_trigger(); - exit(errno); + _exit(errno); } static int spawn_child_flag(struct child *child, bool clone_vm)