mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
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:
commit
140fa23df9
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user