]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests/bpf: Keep int return type for tailcall subprogs
authorYonghong Song <yonghong.song@linux.dev>
Tue, 9 Jun 2026 23:34:07 +0000 (16:34 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 10 Jun 2026 04:21:15 +0000 (21:21 -0700)
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>
Link: https://lore.kernel.org/r/20260609233407.2711577-1-yonghong.song@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/progs/tailcall_bpf2bpf2.c
tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy1.c
tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy2.c
tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy3.c
tools/testing/selftests/bpf/progs/tailcall_bpf2bpf_hierarchy_fentry.c
tools/testing/selftests/bpf/progs/verifier_sock.c

index ce97d141daee15e7625d4ef2b48ec1f2469cd105..c4fadee5aadc6bedb522b716f545990d2e18fa8a 100644 (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;
index d556b19413d7b7690bb4fe92cf3d3938056a2f55..1fd07824d88aefa7379d18d59c1a20391d8dcd52 100644 (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;
 }
index ae94c9c70ab7d5e54a90fafb16283c988f441f52..6fde0ab921486cc6f065dc8905b73f50fc6d5912 100644 (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;
 }
index 56b6b009984072291fdbcc9df1b95baf90f454ce..0ef9cfb2da8dae9fc5eb148df85bbf3d788a71ef 100644 (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;
 }
 
index 5261395713cd5856e6a690963e0aa386bb3386b8..6db9afee20950399340c5d67876cb2fc3ee082e0 100644 (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;
 }
index 9f680cf44512e76dd78ce582b1308699321400b4..4f2f3209eec818cfe5857a7c4ca620f51c83cdc5 100644 (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;
 }