* [PATCH bpf-next v2 0/2] selftests/bpf: Fix tests for llvm23 true signature
@ 2026-06-09 23:34 Yonghong Song
2026-06-09 23:34 ` [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs Yonghong Song
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Yonghong Song @ 2026-06-09 23:34 UTC (permalink / raw)
To: bpf
Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team,
Martin KaFai Lau
LLVM23 ([1]) records the 'true' function signature in BTF, i.e. the
signature inferred after optimization rather than the one written in C.
This caused two kinds of selftest failures (see below).
Case 1: keep int return type for tailcall subprogs
The verifier requires any subprog that issues a bpf_tail_call to return
an 'int' (see check_btf_func() in kernel/bpf/check_btf.c, which rejects
it with "tail_call is only allowed in functions that return 'int'").
Several tailcall subprogs do 'return 0' (or another constant) whose
result no caller uses. With llvm23 the compiler folds the constant and,
since the return value is dead, optimizes the subprog to effectively
return 'void' and records 'void' in BTF, so the program fails to load.
Use barrier_var() and __sink() to prevent returned value from being
optimized.
Case 2: adjust tracing prog ctx layout for the true signature
test_pkt_access_subprog2() has an unused argument that llvm optimizes
away. Before llvm23 the BTF signature did not match the optimized
assembly, so the verifier fell back to MAX_BPF_FUNC_REG_ARGS (5) u64
arguments and the fexit return value sat after args[5]. With llvm23 the
true signature has a single argument, so the return value moves to the
slot after args[1]. Select the matching ctx struct based on __clang_major__
so the test works with both old and new llvm.
[1] https://github.com/llvm/llvm-project/pull/198426
Changelogs:
v1 -> v2:
- v1: https://lore.kernel.org/bpf/20260609163947.1717694-1-yonghong.song@linux.dev/
- Do not use bpf array map or bpf global var. Use __sink() instead.
Yonghong Song (2):
selftests/bpf: Keep int return type for tailcall subprogs
selftests/bpf: Adjust fexit_bpf2bpf ctx layout for llvm23 true
signature
.../selftests/bpf/progs/fexit_bpf2bpf.c | 13 ++++++++--
.../selftests/bpf/progs/tailcall_bpf2bpf2.c | 5 +++-
.../bpf/progs/tailcall_bpf2bpf_hierarchy1.c | 13 ++++++----
.../bpf/progs/tailcall_bpf2bpf_hierarchy2.c | 24 +++++++++++++------
.../bpf/progs/tailcall_bpf2bpf_hierarchy3.c | 13 +++++++---
.../progs/tailcall_bpf2bpf_hierarchy_fentry.c | 13 +++++++---
.../selftests/bpf/progs/verifier_sock.c | 9 +++++--
7 files changed, 68 insertions(+), 22 deletions(-)
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs 2026-06-09 23:34 [PATCH bpf-next v2 0/2] selftests/bpf: Fix tests for llvm23 true signature Yonghong Song @ 2026-06-09 23:34 ` Yonghong Song 2026-06-09 23:42 ` sashiko-bot 2026-06-10 0:07 ` bot+bpf-ci 2026-06-09 23:34 ` [PATCH bpf-next v2 2/2] selftests/bpf: Adjust fexit_bpf2bpf ctx layout for llvm23 true signature Yonghong Song 2026-06-10 4:30 ` [PATCH bpf-next v2 0/2] selftests/bpf: Fix tests " patchwork-bot+netdevbpf 2 siblings, 2 replies; 9+ messages in thread From: Yonghong Song @ 2026-06-09 23:34 UTC (permalink / raw) To: bpf Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team, Martin KaFai Lau LLVM23 ([1]) supports 'true' function signature in BTF. The return type of the caller of a tailcall must be an 'int'. Otherwise, verification will fail (see check_btf_func() in check_btf.c). So with llvm23, it is possible that the compiler may change the caller's return type from 'int' to 'void'. To prevent this, barrier_var() and __sink() are used to avoid returning a constant prone to be optimized. [1] https://github.com/llvm/llvm-project/pull/198426 Signed-off-by: Yonghong Song <yonghong.song@linux.dev> --- .../selftests/bpf/progs/tailcall_bpf2bpf2.c | 5 +++- .../bpf/progs/tailcall_bpf2bpf_hierarchy1.c | 13 ++++++---- .../bpf/progs/tailcall_bpf2bpf_hierarchy2.c | 24 +++++++++++++------ .../bpf/progs/tailcall_bpf2bpf_hierarchy3.c | 13 +++++++--- .../progs/tailcall_bpf2bpf_hierarchy_fentry.c | 13 +++++++--- .../selftests/bpf/progs/verifier_sock.c | 9 +++++-- 6 files changed, 57 insertions(+), 20 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c index ce97d141daee..c4fadee5aadc 100644 --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c @@ -13,11 +13,14 @@ struct { static __noinline int subprog_tail(struct __sk_buff *skb) { + int ret = 1; + if (load_byte(skb, 0)) bpf_tail_call_static(skb, &jmp_table, 1); else bpf_tail_call_static(skb, &jmp_table, 0); - return 1; + barrier_var(ret); + return ret; } int count = 0; diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy1.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy1.c index d556b19413d7..1fd07824d88a 100644 --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy1.c +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy1.c @@ -16,20 +16,25 @@ int count = 0; static __noinline int subprog_tail(struct __sk_buff *skb) { + int ret = 0; + bpf_tail_call_static(skb, &jmp_table, 0); - return 0; + barrier_var(ret); + return ret; } SEC("tc") int entry(struct __sk_buff *skb) { - int ret = 1; + int ret = 1, ret1, ret2; clobber_regs_stack(); count++; - subprog_tail(skb); - subprog_tail(skb); + ret1 = subprog_tail(skb); + ret2 = subprog_tail(skb); + __sink(ret1); + __sink(ret2); return ret; } diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c index ae94c9c70ab7..6fde0ab92148 100644 --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c @@ -25,8 +25,11 @@ int count1 = 0; static __noinline int subprog_tail0(struct __sk_buff *skb) { + int ret = 0; + bpf_tail_call_static(skb, &jmp_table, 0); - return 0; + barrier_var(ret); + return ret; } __auxiliary @@ -41,16 +44,22 @@ int classifier_0(struct __sk_buff *skb) static __noinline int subprog_tail1(struct __sk_buff *skb) { + int ret = 0; + bpf_tail_call_static(skb, &jmp_table, 1); - return 0; + barrier_var(ret); + return ret; } __auxiliary SEC("tc") int classifier_1(struct __sk_buff *skb) { + int ret; + count1++; - subprog_tail1(skb); + ret = subprog_tail1(skb); + __sink(ret); return 0; } @@ -59,13 +68,14 @@ __retval(33) SEC("tc") int tailcall_bpf2bpf_hierarchy_2(struct __sk_buff *skb) { - int ret = 0; + int ret = 0, ret1, ret2; clobber_regs_stack(); - subprog_tail0(skb); - subprog_tail1(skb); - + ret1 = subprog_tail0(skb); + ret2 = subprog_tail1(skb); + __sink(ret1); + __sink(ret2); __sink(ret); return (count1 << 16) | count0; } diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy3.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy3.c index 56b6b0099840..0ef9cfb2da8d 100644 --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy3.c +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy3.c @@ -33,17 +33,24 @@ int count = 0; static __noinline int subprog_tail(struct __sk_buff *skb, void *jmp_table) { + int ret = 0; + bpf_tail_call_static(skb, jmp_table, 0); - return 0; + barrier_var(ret); + return ret; } __auxiliary SEC("tc") int classifier_0(struct __sk_buff *skb) { + int ret1, ret2; + count++; - subprog_tail(skb, &jmp_table0); - subprog_tail(skb, &jmp_table1); + ret1 = subprog_tail(skb, &jmp_table0); + ret2 = subprog_tail(skb, &jmp_table1); + __sink(ret1); + __sink(ret2); return count; } diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_fentry.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_fentry.c index 5261395713cd..6db9afee2095 100644 --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_fentry.c +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_fentry.c @@ -18,18 +18,25 @@ int count = 0; static __noinline int subprog_tail(void *ctx) { + int ret = 0; + bpf_tail_call_static(ctx, &jmp_table, 0); - return 0; + barrier_var(ret); + return ret; } SEC("fentry/dummy") int BPF_PROG(fentry, struct sk_buff *skb) { + int ret1, ret2; + clobber_regs_stack(); count++; - subprog_tail(ctx); - subprog_tail(ctx); + ret1 = subprog_tail(ctx); + ret2 = subprog_tail(ctx); + __sink(ret1); + __sink(ret2); return 0; } diff --git a/tools/testing/selftests/bpf/progs/verifier_sock.c b/tools/testing/selftests/bpf/progs/verifier_sock.c index 9f680cf44512..4f2f3209eec8 100644 --- a/tools/testing/selftests/bpf/progs/verifier_sock.c +++ b/tools/testing/selftests/bpf/progs/verifier_sock.c @@ -1120,8 +1120,11 @@ int tail_call(struct __sk_buff *sk) static __noinline int static_tail_call(struct __sk_buff *sk) { + int ret = 0; + bpf_tail_call_static(sk, &jmp_table, 0); - return 0; + barrier_var(ret); + return ret; } /* Tail calls in sub-programs invalidate packet pointers. */ @@ -1144,10 +1147,12 @@ __failure __msg("invalid mem access") int invalidate_pkt_pointers_by_static_tail_call(struct __sk_buff *sk) { int *p = (void *)(long)sk->data; + int ret; if ((void *)(p + 1) > (void *)(long)sk->data_end) return TCX_DROP; - static_tail_call(sk); + ret = static_tail_call(sk); + __sink(ret); *p = 42; /* this is unsafe */ return TCX_PASS; } -- 2.53.0-Meta ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs 2026-06-09 23:34 ` [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs Yonghong Song @ 2026-06-09 23:42 ` sashiko-bot 2026-06-10 3:41 ` Yonghong Song 2026-06-10 4:06 ` Yonghong Song 2026-06-10 0:07 ` bot+bpf-ci 1 sibling, 2 replies; 9+ messages in thread From: sashiko-bot @ 2026-06-09 23:42 UTC (permalink / raw) To: Yonghong Song; +Cc: bpf Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] Incomplete application of compiler optimization mitigation in `classifier_0`. -- commit 230de7228ec64290b0e30a90f68ca96dd2ee6dd0 Author: Yonghong Song <yonghong.song@linux.dev> selftests/bpf: Keep int return type for tailcall subprogs This commit updates tailcall subprograms to prevent LLVM 23 from optimizing their return types from 'int' to 'void', which would cause verifier failures. It uses barrier_var() and __sink() to ensure the compiler retains the int return type. > diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c > index ae94c9c70ab7d..6fde0ab921486 100644 > --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c > +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c [ ... ] > @@ -41,16 +44,22 @@ int classifier_0(struct __sk_buff *skb) > static __noinline > int subprog_tail1(struct __sk_buff *skb) > { > + int ret = 0; > + > bpf_tail_call_static(skb, &jmp_table, 1); > - return 0; > + barrier_var(ret); > + return ret; > } > > __auxiliary > SEC("tc") > int classifier_1(struct __sk_buff *skb) > { > + int ret; > + > count1++; > - subprog_tail1(skb); > + ret = subprog_tail1(skb); > + __sink(ret); > return 0; > } [Severity: Medium] In classifier_1() here, the return value of subprog_tail1() is captured and sunk using __sink() to prevent optimization. Should classifier_0() also be updated symmetrically to capture and sink the return value of subprog_tail0()? Looking at classifier_0() in the same file: __auxiliary SEC("tc") int classifier_0(struct __sk_buff *skb) { count0++; subprog_tail0(skb); return 0; } While the return value of subprog_tail0() is currently sunk elsewhere in tailcall_bpf2bpf_hierarchy_2(), leaving this callsite unsunk could potentially leave the code fragile to LLVM optimizations that might create a void-returning clone of subprog_tail0() specifically for classifier_0(). -- Sashiko AI review · https://sashiko.dev/#/patchset/20260609233402.2711071-1-yonghong.song@linux.dev?part=1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs 2026-06-09 23:42 ` sashiko-bot @ 2026-06-10 3:41 ` Yonghong Song 2026-06-10 4:06 ` Yonghong Song 1 sibling, 0 replies; 9+ messages in thread From: Yonghong Song @ 2026-06-10 3:41 UTC (permalink / raw) To: sashiko-reviews; +Cc: bpf On 6/9/26 4:42 PM, sashiko-bot@kernel.org wrote: > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: > - [Medium] Incomplete application of compiler optimization mitigation in `classifier_0`. > -- > > commit 230de7228ec64290b0e30a90f68ca96dd2ee6dd0 > Author: Yonghong Song <yonghong.song@linux.dev> > selftests/bpf: Keep int return type for tailcall subprogs > > This commit updates tailcall subprograms to prevent LLVM 23 from optimizing > their return types from 'int' to 'void', which would cause verifier failures. > It uses barrier_var() and __sink() to ensure the compiler retains the int > return type. > >> diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c >> index ae94c9c70ab7d..6fde0ab921486 100644 >> --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c >> +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c > [ ... ] >> @@ -41,16 +44,22 @@ int classifier_0(struct __sk_buff *skb) >> static __noinline >> int subprog_tail1(struct __sk_buff *skb) >> { >> + int ret = 0; >> + >> bpf_tail_call_static(skb, &jmp_table, 1); >> - return 0; >> + barrier_var(ret); >> + return ret; >> } >> >> __auxiliary >> SEC("tc") >> int classifier_1(struct __sk_buff *skb) >> { >> + int ret; >> + >> count1++; >> - subprog_tail1(skb); >> + ret = subprog_tail1(skb); >> + __sink(ret); >> return 0; >> } > [Severity: Medium] > In classifier_1() here, the return value of subprog_tail1() is captured and > sunk using __sink() to prevent optimization. Should classifier_0() also be > updated symmetrically to capture and sink the return value of subprog_tail0()? > > Looking at classifier_0() in the same file: > > __auxiliary > SEC("tc") > int classifier_0(struct __sk_buff *skb) > { > count0++; > subprog_tail0(skb); > return 0; > } > > While the return value of subprog_tail0() is currently sunk elsewhere in > tailcall_bpf2bpf_hierarchy_2(), leaving this callsite unsunk could > potentially leave the code fragile to LLVM optimizations that might create a > void-returning clone of subprog_tail0() specifically for classifier_0(). We should be fine. In the same file, we have SEC("tc") int tailcall_bpf2bpf_hierarchy_2(struct __sk_buff *skb) { - int ret = 0; + int ret = 0, ret1, ret2; clobber_regs_stack(); - subprog_tail0(skb); - subprog_tail1(skb); - + ret1 = subprog_tail0(skb); + ret2 = subprog_tail1(skb); + __sink(ret1); + __sink(ret2); __sink(ret); return (count1 << 16) | count0; } This will guarantee subprog_tail0() will have a return value. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs 2026-06-09 23:42 ` sashiko-bot 2026-06-10 3:41 ` Yonghong Song @ 2026-06-10 4:06 ` Yonghong Song 1 sibling, 0 replies; 9+ messages in thread From: Yonghong Song @ 2026-06-10 4:06 UTC (permalink / raw) To: sashiko-reviews; +Cc: bpf On 6/9/26 4:42 PM, sashiko-bot@kernel.org wrote: > Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: > - [Medium] Incomplete application of compiler optimization mitigation in `classifier_0`. > -- > > commit 230de7228ec64290b0e30a90f68ca96dd2ee6dd0 > Author: Yonghong Song <yonghong.song@linux.dev> > selftests/bpf: Keep int return type for tailcall subprogs > > This commit updates tailcall subprograms to prevent LLVM 23 from optimizing > their return types from 'int' to 'void', which would cause verifier failures. > It uses barrier_var() and __sink() to ensure the compiler retains the int > return type. > >> diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c >> index ae94c9c70ab7d..6fde0ab921486 100644 >> --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c >> +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c > [ ... ] >> @@ -41,16 +44,22 @@ int classifier_0(struct __sk_buff *skb) >> static __noinline >> int subprog_tail1(struct __sk_buff *skb) >> { >> + int ret = 0; >> + >> bpf_tail_call_static(skb, &jmp_table, 1); >> - return 0; >> + barrier_var(ret); >> + return ret; >> } >> >> __auxiliary >> SEC("tc") >> int classifier_1(struct __sk_buff *skb) >> { >> + int ret; >> + >> count1++; >> - subprog_tail1(skb); >> + ret = subprog_tail1(skb); >> + __sink(ret); >> return 0; >> } > [Severity: Medium] > In classifier_1() here, the return value of subprog_tail1() is captured and > sunk using __sink() to prevent optimization. Should classifier_0() also be > updated symmetrically to capture and sink the return value of subprog_tail0()? > > Looking at classifier_0() in the same file: > > __auxiliary > SEC("tc") > int classifier_0(struct __sk_buff *skb) > { > count0++; > subprog_tail0(skb); > return 0; > } > > While the return value of subprog_tail0() is currently sunk elsewhere in > tailcall_bpf2bpf_hierarchy_2(), leaving this callsite unsunk could > potentially leave the code fragile to LLVM optimizations that might create a > void-returning clone of subprog_tail0() specifically for classifier_0(). clone is possible. But I would say it is extremely unlikely. In https://github.com/llvm/llvm-project/pull/165310, for all kernel functions, I only find one clone (specialization) with thin-lto mode. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs 2026-06-09 23:34 ` [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs Yonghong Song 2026-06-09 23:42 ` sashiko-bot @ 2026-06-10 0:07 ` bot+bpf-ci 2026-06-10 3:45 ` Yonghong Song 1 sibling, 1 reply; 9+ messages in thread From: bot+bpf-ci @ 2026-06-10 0:07 UTC (permalink / raw) To: yonghong.song, bpf Cc: ast, andrii, daniel, kernel-team, martin.lau, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, clm, ihor.solodrai [-- Attachment #1: Type: text/plain, Size: 2477 bytes --] > diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c > index ce97d141daee..c4fadee5aadc 100644 > --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c > +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c [ ... ] > diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c > index ae94c9c70ab7..6fde0ab92148 100644 > --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c > +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c > @@ -25,8 +25,11 @@ int count1 = 0; > static __noinline > int subprog_tail0(struct __sk_buff *skb) > { > + int ret = 0; > + > bpf_tail_call_static(skb, &jmp_table, 0); > - return 0; > + barrier_var(ret); > + return ret; > } > > __auxiliary > SEC("tc") > int classifier_0(struct __sk_buff *skb) > { > count0++; > subprog_tail0(skb); > return 0; > } The patch adds barrier_var() to subprog_tail0() to prevent the compiler from optimizing its return type from int to void. However, is the approach sufficient for classifier_0()? Looking at how the patch handles other callers like classifier_1(): > @@ -41,16 +44,22 @@ int classifier_0(struct __sk_buff *skb) > static __noinline > int subprog_tail1(struct __sk_buff *skb) > { > + int ret = 0; > + > bpf_tail_call_static(skb, &jmp_table, 1); > - return 0; > + barrier_var(ret); > + return ret; > } > > __auxiliary > SEC("tc") > int classifier_1(struct __sk_buff *skb) > { > + int ret; > + > count1++; > - subprog_tail1(skb); > + ret = subprog_tail1(skb); > + __sink(ret); > return 0; > } In classifier_1(), the return value is captured and passed to __sink(). This creates a use at the callsite that forces the compiler to preserve the int return type. Without capturing and sinking the return value in classifier_0(), could LLVM still create a void-returning clone of subprog_tail0() for this specific callsite? While subprog_tail0() is also called from tailcall_bpf2bpf_hierarchy_2() where its return value is properly sunk, the compiler might still specialize the function differently for the two callsites. [ ... ] --- AI reviewed your patch. Please fix the bug or email reply why it's not a bug. See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27243286945 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs 2026-06-10 0:07 ` bot+bpf-ci @ 2026-06-10 3:45 ` Yonghong Song 0 siblings, 0 replies; 9+ messages in thread From: Yonghong Song @ 2026-06-10 3:45 UTC (permalink / raw) To: bot+bpf-ci, bpf Cc: ast, andrii, daniel, kernel-team, martin.lau, eddyz87, clm, ihor.solodrai On 6/9/26 5:07 PM, bot+bpf-ci@kernel.org wrote: >> diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c >> index ce97d141daee..c4fadee5aadc 100644 >> --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c >> +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c > [ ... ] > >> diff --git a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c >> index ae94c9c70ab7..6fde0ab92148 100644 >> --- a/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c >> +++ b/tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c >> @@ -25,8 +25,11 @@ int count1 = 0; >> static __noinline >> int subprog_tail0(struct __sk_buff *skb) >> { >> + int ret = 0; >> + >> bpf_tail_call_static(skb, &jmp_table, 0); >> - return 0; >> + barrier_var(ret); >> + return ret; >> } >> >> __auxiliary >> SEC("tc") >> int classifier_0(struct __sk_buff *skb) >> { >> count0++; >> subprog_tail0(skb); >> return 0; >> } > The patch adds barrier_var() to subprog_tail0() to prevent the compiler > from optimizing its return type from int to void. However, is the > approach sufficient for classifier_0()? > > Looking at how the patch handles other callers like classifier_1(): > >> @@ -41,16 +44,22 @@ int classifier_0(struct __sk_buff *skb) >> static __noinline >> int subprog_tail1(struct __sk_buff *skb) >> { >> + int ret = 0; >> + >> bpf_tail_call_static(skb, &jmp_table, 1); >> - return 0; >> + barrier_var(ret); >> + return ret; >> } >> >> __auxiliary >> SEC("tc") >> int classifier_1(struct __sk_buff *skb) >> { >> + int ret; >> + >> count1++; >> - subprog_tail1(skb); >> + ret = subprog_tail1(skb); >> + __sink(ret); >> return 0; >> } > In classifier_1(), the return value is captured and passed to __sink(). > This creates a use at the callsite that forces the compiler to preserve > the int return type. > > Without capturing and sinking the return value in classifier_0(), could > LLVM still create a void-returning clone of subprog_tail0() for this > specific callsite? While subprog_tail0() is also called from > tailcall_bpf2bpf_hierarchy_2() where its return value is properly sunk, > the compiler might still specialize the function differently for the two > callsites. We should be fine. In the same file, we have SEC("tc") __success __retval(33) SEC("tc") int tailcall_bpf2bpf_hierarchy_2(struct __sk_buff *skb) { int ret = 0, ret1, ret2; clobber_regs_stack(); ret1 = subprog_tail0(skb); ret2 = subprog_tail1(skb); __sink(ret1); __sink(ret2); __sink(ret); return (count1 << 16) | count0; } This will guarantee subprog_tail0() will have a return value. > > [ ... ] > > > --- > AI reviewed your patch. Please fix the bug or email reply why it's not a bug. > See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md > > CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27243286945 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH bpf-next v2 2/2] selftests/bpf: Adjust fexit_bpf2bpf ctx layout for llvm23 true signature 2026-06-09 23:34 [PATCH bpf-next v2 0/2] selftests/bpf: Fix tests for llvm23 true signature Yonghong Song 2026-06-09 23:34 ` [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs Yonghong Song @ 2026-06-09 23:34 ` Yonghong Song 2026-06-10 4:30 ` [PATCH bpf-next v2 0/2] selftests/bpf: Fix tests " patchwork-bot+netdevbpf 2 siblings, 0 replies; 9+ messages in thread From: Yonghong Song @ 2026-06-09 23:34 UTC (permalink / raw) To: bpf Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann, kernel-team, Martin KaFai Lau test_pkt_access_subprog2() is defined in C as int test_pkt_access_subprog2(int val, volatile struct __sk_buff *skb) but llvm optimizes away the unused 'int val' argument. Before llvm23 the BTF signature did not match the optimized assembly, so the verifier set attach_func_proto to NULL and fell back to MAX_BPF_FUNC_REG_ARGS (5) u64 arguments (see btf_ctx_access()). The fexit ctx struct therefore placed the return value after args[5]. With llvm23 the 'true' signature int test_pkt_access_subprog2(volatile struct __sk_buff *skb) is recorded in BTF, so nr_args becomes 1 and the return value moves to the slot right after args[1]. Select the matching args_subprog2 layout based on __clang_major__ so the test works with both old and new llvm. Signed-off-by: Yonghong Song <yonghong.song@linux.dev> --- tools/testing/selftests/bpf/progs/fexit_bpf2bpf.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/fexit_bpf2bpf.c b/tools/testing/selftests/bpf/progs/fexit_bpf2bpf.c index 983b7c233382..f4bbf87b82dd 100644 --- a/tools/testing/selftests/bpf/progs/fexit_bpf2bpf.c +++ b/tools/testing/selftests/bpf/progs/fexit_bpf2bpf.c @@ -53,14 +53,23 @@ int BPF_PROG(test_subprog1, struct sk_buff *skb, int ret) * r0 = *(u32 *)(r1 + 0) * w0 <<= 1 * exit - * In such case the verifier falls back to conservative and + * Before llvm23, in such case the verifier falls back to conservative and * tracing program can access arguments and return value as u64 - * instead of accurate types. + * instead of accurate types. With llvm23, the true signature + * int test_pkt_access_subprog2(volatile struct __sk_buff *skb) + * is available in btf. */ +#if __clang_major__ >= 23 +struct args_subprog2 { + __u64 args[1]; + __u64 ret; +}; +#else struct args_subprog2 { __u64 args[5]; __u64 ret; }; +#endif __u64 test_result_subprog2 = 0; SEC("fexit/test_pkt_access_subprog2") int test_subprog2(struct args_subprog2 *ctx) -- 2.53.0-Meta ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH bpf-next v2 0/2] selftests/bpf: Fix tests for llvm23 true signature 2026-06-09 23:34 [PATCH bpf-next v2 0/2] selftests/bpf: Fix tests for llvm23 true signature Yonghong Song 2026-06-09 23:34 ` [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs Yonghong Song 2026-06-09 23:34 ` [PATCH bpf-next v2 2/2] selftests/bpf: Adjust fexit_bpf2bpf ctx layout for llvm23 true signature Yonghong Song @ 2026-06-10 4:30 ` patchwork-bot+netdevbpf 2 siblings, 0 replies; 9+ messages in thread From: patchwork-bot+netdevbpf @ 2026-06-10 4:30 UTC (permalink / raw) To: Yonghong Song; +Cc: bpf, ast, andrii, daniel, kernel-team, martin.lau Hello: This series was applied to bpf/bpf-next.git (master) by Alexei Starovoitov <ast@kernel.org>: On Tue, 9 Jun 2026 16:34:02 -0700 you wrote: > LLVM23 ([1]) records the 'true' function signature in BTF, i.e. the > signature inferred after optimization rather than the one written in C. > This caused two kinds of selftest failures (see below). > > Case 1: keep int return type for tailcall subprogs > > The verifier requires any subprog that issues a bpf_tail_call to return > an 'int' (see check_btf_func() in kernel/bpf/check_btf.c, which rejects > it with "tail_call is only allowed in functions that return 'int'"). > > [...] Here is the summary with links: - [bpf-next,v2,1/2] selftests/bpf: Keep int return type for tailcall subprogs https://git.kernel.org/bpf/bpf-next/c/be1d838b88e4 - [bpf-next,v2,2/2] selftests/bpf: Adjust fexit_bpf2bpf ctx layout for llvm23 true signature https://git.kernel.org/bpf/bpf-next/c/e775c522a455 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] 9+ messages in thread
end of thread, other threads:[~2026-06-10 4:30 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-09 23:34 [PATCH bpf-next v2 0/2] selftests/bpf: Fix tests for llvm23 true signature Yonghong Song 2026-06-09 23:34 ` [PATCH bpf-next v2 1/2] selftests/bpf: Keep int return type for tailcall subprogs Yonghong Song 2026-06-09 23:42 ` sashiko-bot 2026-06-10 3:41 ` Yonghong Song 2026-06-10 4:06 ` Yonghong Song 2026-06-10 0:07 ` bot+bpf-ci 2026-06-10 3:45 ` Yonghong Song 2026-06-09 23:34 ` [PATCH bpf-next v2 2/2] selftests/bpf: Adjust fexit_bpf2bpf ctx layout for llvm23 true signature Yonghong Song 2026-06-10 4:30 ` [PATCH bpf-next v2 0/2] selftests/bpf: Fix tests " 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