mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
selftests/bpf: Test kfuncs returning arena pointers by value
Cover the by-value struct returns a kfunc may now make: two arena pointers filling R0:R2, and an arena pointer beside a scalar. Two further cases drop the tag from one member of a struct and one arm of a union, and stay rejected naming that member, so what decides is the tag rather than the member being a pointer. The existing cases for a struct and a nested struct carrying a plain pointer stay rejected as well. These cases call the kfuncs from C, so the compiler lowers the by-value return itself, and a struct or union only lands in R0:R2 with the LLVM 23 BPF ABI. An older clang, and gcc, return it through a hidden pointer in R1 instead, which shifts the arguments along and fails verification. The file is therefore guarded on LLVM 23, falling back to a dummy test. Signed-off-by: Yonghong Song <yonghong.song@linux.dev> Link: https://lore.kernel.org/r/20260829061610.1699950-1-yonghong.song@linux.dev Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
18bc618388
commit
f7213961e8
|
|
@ -1,11 +1,53 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
|
||||
#include <test_progs.h>
|
||||
#include <bpf/btf.h>
|
||||
#include "aggregate_ret_func.skel.h"
|
||||
#include "aggregate_ret_kfunc.skel.h"
|
||||
#include "aggregate_ret_kfunc_arena.skel.h"
|
||||
|
||||
static bool testmod_has_arena_tagged_member(void)
|
||||
{
|
||||
struct btf *vmlinux_btf, *module_btf = NULL;
|
||||
const struct btf_type *t;
|
||||
bool tagged = false;
|
||||
__s32 id;
|
||||
|
||||
vmlinux_btf = btf__load_vmlinux_btf();
|
||||
if (!vmlinux_btf)
|
||||
return false;
|
||||
|
||||
module_btf = btf__load_module_btf("bpf_testmod", vmlinux_btf);
|
||||
if (!module_btf)
|
||||
goto out;
|
||||
|
||||
/* prog_test_ret_arena::a is 'void __arena_tag *': PTR -> TYPE_TAG -> void */
|
||||
id = btf__find_by_name_kind(module_btf, "prog_test_ret_arena", BTF_KIND_STRUCT);
|
||||
if (id <= 0)
|
||||
goto out;
|
||||
|
||||
t = btf__type_by_id(module_btf, btf_members(btf__type_by_id(module_btf, id))[0].type);
|
||||
if (!t || !btf_is_ptr(t))
|
||||
goto out;
|
||||
|
||||
t = btf__type_by_id(module_btf, t->type);
|
||||
tagged = t && btf_is_type_tag(t) &&
|
||||
!strcmp(btf__name_by_offset(module_btf, t->name_off), "arena");
|
||||
|
||||
out:
|
||||
btf__free(module_btf);
|
||||
btf__free(vmlinux_btf);
|
||||
|
||||
return tagged;
|
||||
}
|
||||
|
||||
void test_aggregate_ret(void)
|
||||
{
|
||||
RUN_TESTS(aggregate_ret_func);
|
||||
RUN_TESTS(aggregate_ret_kfunc);
|
||||
|
||||
if (testmod_has_arena_tagged_member())
|
||||
RUN_TESTS(aggregate_ret_kfunc_arena);
|
||||
else
|
||||
test__skip();
|
||||
}
|
||||
|
|
|
|||
121
tools/testing/selftests/bpf/progs/aggregate_ret_kfunc_arena.c
Normal file
121
tools/testing/selftests/bpf/progs/aggregate_ret_kfunc_arena.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf_arena_common.h>
|
||||
#include "bpf_misc.h"
|
||||
#include "../test_kmods/bpf_testmod_kfunc.h"
|
||||
|
||||
#if defined(__clang_major__) && __clang_major__ >= 23
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARENA);
|
||||
__uint(map_flags, BPF_F_MMAPABLE);
|
||||
__uint(max_entries, 2);
|
||||
} arena SEC(".maps");
|
||||
|
||||
SEC("syscall")
|
||||
__arch_x86_64 __arch_arm64
|
||||
__load_if_JITed()
|
||||
__success __retval(0)
|
||||
int aggregate_ret_kfunc_arena(void *ctx)
|
||||
{
|
||||
u32 volatile __arena *page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
|
||||
u32 volatile __arena *a, *b;
|
||||
struct prog_test_ret_arena r;
|
||||
|
||||
if (!page)
|
||||
return 1;
|
||||
|
||||
/* Both halves come back in R0:R2, pointing at page and page + 4. */
|
||||
r = bpf_kfunc_call_test_ret_arena((u64)page);
|
||||
if (!r.a || !r.b)
|
||||
return 2;
|
||||
|
||||
a = (u32 __arena *)r.a;
|
||||
b = (u32 __arena *)r.b;
|
||||
*a = 1;
|
||||
*b = 2;
|
||||
if (*a != 1)
|
||||
return 3;
|
||||
if (*b != 2)
|
||||
return 4;
|
||||
|
||||
page[0] = 7;
|
||||
if (*a != 7)
|
||||
return 5;
|
||||
page[1] = 9;
|
||||
if (*b != 9)
|
||||
return 6;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__arch_x86_64 __arch_arm64
|
||||
__load_if_JITed()
|
||||
__success __retval(0)
|
||||
int aggregate_ret_kfunc_arena_mixed(void *ctx)
|
||||
{
|
||||
u32 __arena *page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
|
||||
struct prog_test_ret_arena_mixed r;
|
||||
u32 volatile __arena *p;
|
||||
|
||||
if (!page)
|
||||
return 1;
|
||||
|
||||
/* An arena pointer in R0 beside a scalar in R2. */
|
||||
r = bpf_kfunc_call_test_ret_arena_mixed((u64)page);
|
||||
if (!r.p)
|
||||
return 2;
|
||||
if (r.tag != 0xbeef)
|
||||
return 3;
|
||||
|
||||
p = (u32 __arena *)r.p;
|
||||
*p = 3;
|
||||
if (*p != 3)
|
||||
return 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__arch_x86_64 __arch_arm64
|
||||
__failure __msg("is not composed of scalars or arena pointers")
|
||||
__msg("member 'b' has type PTR")
|
||||
int aggregate_ret_kfunc_arena_untagged_fail(void *ctx)
|
||||
{
|
||||
struct prog_test_ret_arena_untagged r;
|
||||
|
||||
r = bpf_kfunc_call_test_ret_arena_untagged(0);
|
||||
|
||||
return r.a == r.b;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__arch_x86_64 __arch_arm64
|
||||
__failure __msg("is not composed of scalars or arena pointers")
|
||||
__msg("member 'b' has type PTR")
|
||||
int aggregate_ret_kfunc_arena_union_fail(void *ctx)
|
||||
{
|
||||
union prog_test_ret_arena_union r;
|
||||
|
||||
r = bpf_kfunc_call_test_ret_arena_union(0);
|
||||
|
||||
return r.a == r.b;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
SEC("socket")
|
||||
__description("aggregate_ret_kfunc_arena: needs LLVM 23, dummy test")
|
||||
__skip("needs LLVM 23")
|
||||
__success
|
||||
int dummy_test(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
|
@ -981,6 +981,34 @@ __bpf_kfunc struct prog_test_ret_ptr bpf_kfunc_call_test_ret_ptr(u64 tag)
|
|||
return r;
|
||||
}
|
||||
|
||||
__bpf_kfunc struct prog_test_ret_arena bpf_kfunc_call_test_ret_arena(u64 addr)
|
||||
{
|
||||
struct prog_test_ret_arena r = { .a = (void *)addr, .b = (void *)(addr + 4) };
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
__bpf_kfunc struct prog_test_ret_arena_mixed bpf_kfunc_call_test_ret_arena_mixed(u64 addr)
|
||||
{
|
||||
struct prog_test_ret_arena_mixed r = { .p = (void *)addr, .tag = 0xbeef };
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
__bpf_kfunc struct prog_test_ret_arena_untagged bpf_kfunc_call_test_ret_arena_untagged(u64 addr)
|
||||
{
|
||||
struct prog_test_ret_arena_untagged r = { .a = (void *)addr, .b = NULL };
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
__bpf_kfunc union prog_test_ret_arena_union bpf_kfunc_call_test_ret_arena_union(u64 addr)
|
||||
{
|
||||
union prog_test_ret_arena_union r = { .a = (void *)addr };
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
__bpf_kfunc struct prog_test_ret_nested bpf_kfunc_call_test_ret_nested(u64 tag)
|
||||
{
|
||||
struct prog_test_ret_nested r = { .in = { .p = NULL }, .tag = tag };
|
||||
|
|
@ -1574,6 +1602,10 @@ BTF_ID_FLAGS(func, bpf_kfunc_call_test_i128)
|
|||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_pair)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_fastcall, KF_FASTCALL)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_ptr)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_arena)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_arena_mixed)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_arena_untagged)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_arena_union)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_nested)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_ptr_arr)
|
||||
BTF_ID_FLAGS(func, bpf_kfunc_call_test_ret_arr_struct)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@ struct prog_test_ref_kfunc {
|
|||
};
|
||||
#endif
|
||||
|
||||
#if __has_attribute(btf_type_tag)
|
||||
#define __arena_tag __attribute__((btf_type_tag("arena")))
|
||||
#else
|
||||
#define __arena_tag
|
||||
#endif
|
||||
|
||||
struct bpf_iter_testmod_seq;
|
||||
|
||||
struct prog_test_pass1 {
|
||||
|
|
@ -70,6 +76,26 @@ struct prog_test_ret_ptr { /* 16 bytes: contains a pointer */
|
|||
__u64 tag;
|
||||
};
|
||||
|
||||
struct prog_test_ret_arena { /* 16 bytes: two arena pointers */
|
||||
void __arena_tag *a;
|
||||
void __arena_tag *b;
|
||||
};
|
||||
|
||||
struct prog_test_ret_arena_mixed { /* 16 bytes: an arena pointer and a scalar */
|
||||
void __arena_tag *p;
|
||||
__u64 tag;
|
||||
};
|
||||
|
||||
struct prog_test_ret_arena_untagged { /* 16 bytes: 'b' lacks the arena tag */
|
||||
void __arena_tag *a;
|
||||
void *b;
|
||||
};
|
||||
|
||||
union prog_test_ret_arena_union { /* 8 bytes: 'b' lacks the arena tag */
|
||||
void __arena_tag *a;
|
||||
void *b;
|
||||
};
|
||||
|
||||
struct prog_test_ret_nested { /* 16 bytes: the pointer hides one level down */
|
||||
struct {
|
||||
void *p;
|
||||
|
|
@ -200,6 +226,10 @@ struct prog_test_ret_nested bpf_kfunc_call_test_ret_nested(__u64 tag) __ksym;
|
|||
struct prog_test_ret_ptr_arr bpf_kfunc_call_test_ret_ptr_arr(void) __ksym;
|
||||
struct prog_test_ret_arr_struct bpf_kfunc_call_test_ret_arr_struct(void) __ksym;
|
||||
struct prog_test_ret_arr2d bpf_kfunc_call_test_ret_arr2d(void) __ksym;
|
||||
struct prog_test_ret_arena bpf_kfunc_call_test_ret_arena(__u64 addr) __ksym;
|
||||
struct prog_test_ret_arena_mixed bpf_kfunc_call_test_ret_arena_mixed(__u64 addr) __ksym;
|
||||
struct prog_test_ret_arena_untagged bpf_kfunc_call_test_ret_arena_untagged(__u64 addr) __ksym;
|
||||
union prog_test_ret_arena_union bpf_kfunc_call_test_ret_arena_union(__u64 addr) __ksym;
|
||||
struct prog_test_ret_deep bpf_kfunc_call_test_ret_deep(__u64 v) __ksym;
|
||||
struct prog_test_ret_big bpf_kfunc_call_test_ret_big(void) __ksym;
|
||||
__u64 bpf_kfunc_call_stack_arg(__u64 a, __u64 b, __u64 c, __u64 d,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user