From: Alexei Starovoitov The initial values of variables in arena point to other variables in arena: pointer points to ------------ ------------------------------------------- px an int parr the third element of an array head, next the elements of a list pbig an element of an array in .bss.addr_space.1 The prog and user space must see the same pointers: - The prog follows each pointer and examines the value that it finds. - User space compares px, parr, head and next with the addresses in the skeleton. - User space reads through pbig the value that the prog stored there. The variables in .bss.addr_space.1 must be zero and aligned, and the object file must be smaller than these variables. A second object has a pointer in arena to a variable that is not in arena. The open of this object must fail. Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/Makefile.skel | 3 +- .../bpf/prog_tests/arena_data_ptrs.c | 67 ++++++++++ .../selftests/bpf/progs/arena_data_ptrs.c | 117 ++++++++++++++++++ .../bpf/progs/arena_data_ptrs_fail.c | 25 ++++ 4 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/arena_data_ptrs.c create mode 100644 tools/testing/selftests/bpf/progs/arena_data_ptrs.c create mode 100644 tools/testing/selftests/bpf/progs/arena_data_ptrs_fail.c diff --git a/tools/testing/selftests/bpf/Makefile.skel b/tools/testing/selftests/bpf/Makefile.skel index e8699903feef..b8eaecc7f429 100644 --- a/tools/testing/selftests/bpf/Makefile.skel +++ b/tools/testing/selftests/bpf/Makefile.skel @@ -20,7 +20,8 @@ ifneq ($(BPF_CC),) BPF_SRCS := $(notdir $(wildcard progs/*.c)) BPF_OBJS := $(patsubst %.c,$(RDIR)/%.bpf.o,$(BPF_SRCS)) -SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c +SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c \ + arena_data_ptrs_fail.c LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h \ linked_vars.skel.h linked_maps.skel.h linked_arena.skel.h \ diff --git a/tools/testing/selftests/bpf/prog_tests/arena_data_ptrs.c b/tools/testing/selftests/bpf/prog_tests/arena_data_ptrs.c new file mode 100644 index 000000000000..79bc324f0909 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/arena_data_ptrs.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +struct elem { + struct elem *next; + int val; +}; + +#include "arena_data_ptrs.skel.h" + +static int run_prog(struct bpf_program *prog) +{ + LIBBPF_OPTS(bpf_test_run_opts, opts); + + if (!ASSERT_OK(bpf_prog_test_run_opts(bpf_program__fd(prog), &opts), "run")) + return -1; + return opts.retval; +} + +static void test_ptrs_and_bss(void) +{ + struct arena_data_ptrs *skel; + size_t elf_sz; + + skel = arena_data_ptrs__open_and_load(); + if (!ASSERT_OK_PTR(skel, "open_and_load")) + return; + + ASSERT_OK(run_prog(skel->progs.use_ptrs), "use_ptrs"); + if (skel->bss->skip) { + printf("%s:SKIP:compiler doesn't support arena_cast\n", __func__); + test__skip(); + goto out; + } + ASSERT_OK(run_prog(skel->progs.use_bss), "use_bss"); + + /* addresses are the same in user space */ + ASSERT_EQ((void *)skel->arena->px, (void *)&skel->arena->x, "px"); + ASSERT_EQ((void *)skel->arena->parr, (void *)&skel->arena->arr[2], "parr"); + ASSERT_EQ((void *)skel->arena->head, (void *)&skel->arena->n1, "head"); + ASSERT_EQ((void *)skel->arena->n1.next, (void *)&skel->arena->n2, "n1.next"); + ASSERT_EQ(*(long *)skel->arena->pbig, 7, "*pbig"); + + /* 'big' is not in the file */ + arena_data_ptrs__elf_bytes(&elf_sz); + ASSERT_LT(elf_sz, 8192 * sizeof(long), "elf_sz"); +out: + arena_data_ptrs__destroy(skel); +} + +static void test_ptr_to_not_arena(void) +{ + struct bpf_object *obj; + + obj = bpf_object__open_file("./arena_data_ptrs_fail.bpf.o", NULL); + if (!ASSERT_ERR_PTR(obj, "open")) + bpf_object__close(obj); +} + +void test_arena_data_ptrs(void) +{ + if (test__start_subtest("ptrs_and_bss")) + test_ptrs_and_bss(); + if (test__start_subtest("ptr_to_not_arena")) + test_ptr_to_not_arena(); +} diff --git a/tools/testing/selftests/bpf/progs/arena_data_ptrs.c b/tools/testing/selftests/bpf/progs/arena_data_ptrs.c new file mode 100644 index 000000000000..76e564b54e49 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/arena_data_ptrs.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define BPF_NO_KFUNC_PROTOTYPES +#include +#include +#include "bpf_experimental.h" +#include + +struct { + __uint(type, BPF_MAP_TYPE_ARENA); + __uint(map_flags, BPF_F_MMAPABLE); + __uint(max_entries, 64); +} arena SEC(".maps"); + +bool skip; + +#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) + +struct elem { + struct elem __arena *next; + int val; +}; + +int __arena x = 42; +int __arena arr[4] = { 1, 2, 3, 4 }; +struct elem __arena n3 = { NULL, 3 }; +struct elem __arena n2 = { &n3, 20 }; +struct elem __arena n1 = { &n2, 100 }; + +/* variables that are zero and take no space in the file */ +#define __arena_bss __arena SEC(".bss.addr_space.1") +char __arena_bss one; +long __arena_bss big[8192]; +char __arena_bss aligned64[64] __attribute__((aligned(64))); + +/* pointers to variables in arena that are stored in variables in arena */ +int __arena * __arena px = &x; +int __arena * __arena parr = &arr[2]; +struct elem __arena * __arena head = &n1; +long __arena * __arena pbig = &big[8000]; + +SEC("syscall") +int use_ptrs(void *ctx) +{ + struct elem __arena *n; + int i, sum = 0; + + if (*px != 42) + return 1; + *px = 43; + if (x != 43) + return 2; + if (parr[0] != 3 || parr[1] != 4) + return 3; + for (n = head, i = 0; n && i < 10; n = n->next, i++) + sum += n->val; + if (sum != 123) + return 4; + return 0; +} + +SEC("syscall") +int use_bss(void *ctx) +{ + unsigned long addr = (unsigned long)&aligned64; + + if (one || big[0] || big[8000] || big[8191] || aligned64[63]) + return 1; + *pbig = 7; + if (big[8000] != 7 || big[7999] || big[8001]) + return 2; + big[8191] = 8; + one = 1; + /* variables that are not zero are where they were */ + if (arr[3] != 4) + return 3; + /* hide the address from the compiler, it knows that '& 63' is 0 */ + asm volatile ("" : "+r"(addr)); + if (addr & 63) + return 4; + return 0; +} + +#else + +/* The skeleton has to have what user space looks at */ +struct elem { + struct elem *next; + int val; +}; + +int __arena_global x; +int __arena_global arr[4]; +struct elem __arena_global n2; +struct elem __arena_global n1; +int * __arena_global px; +int * __arena_global parr; +struct elem * __arena_global head; +long * __arena_global pbig; + +SEC("syscall") +int use_ptrs(void *ctx) +{ + skip = true; + return 0; +} + +SEC("syscall") +int use_bss(void *ctx) +{ + skip = true; + return 0; +} + +#endif + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/arena_data_ptrs_fail.c b/tools/testing/selftests/bpf/progs/arena_data_ptrs_fail.c new file mode 100644 index 000000000000..93533dea5f71 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/arena_data_ptrs_fail.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 + +#define BPF_NO_KFUNC_PROTOTYPES +#include +#include +#include "bpf_experimental.h" +#include + +struct { + __uint(type, BPF_MAP_TYPE_ARENA); + __uint(map_flags, BPF_F_MMAPABLE); + __uint(max_entries, 1); +} arena SEC(".maps"); + +/* the pointer is in arena, what it points to is not */ +int y = 1; +int *py SEC(".addr_space.1") = &y; + +SEC("syscall") +int prog(void *ctx) +{ + return y; +} + +char _license[] SEC("license") = "GPL"; -- 2.55.0