Merge branch 'selftests-bpf-fix-tests-for-llvm23-true-signature'

Yonghong Song says:

====================
selftests/bpf: Fix tests for llvm23 true signature

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.
====================

Link: https://patch.msgid.link/20260609233402.2711071-1-yonghong.song@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2026-06-09 21:21:15 -07:00
commit 140fa23df9
7 changed files with 68 additions and 22 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}