Skip to content

Commit 80a4129

Browse files
Alexei Starovoitovanakryiko
authored andcommitted
selftests/bpf: Add unit tests for bpf_arena_alloc/free_pages
Add unit tests for bpf_arena_alloc/free_pages() functionality and bpf_arena_common.h with a set of common helpers and macros that is used in this test and the following patches. Also modify test_loader that didn't support running bpf_prog_type_syscall programs. Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 204c628 commit 80a4129

File tree

6 files changed

+227
-2
lines changed

6 files changed

+227
-2
lines changed

tools/testing/selftests/bpf/DENYLIST.aarch64

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ fill_link_info/kprobe_multi_link_info # bpf_program__attach_kprobe_mu
1010
fill_link_info/kretprobe_multi_link_info # bpf_program__attach_kprobe_multi_opts unexpected error: -95
1111
fill_link_info/kprobe_multi_invalid_ubuff # bpf_program__attach_kprobe_multi_opts unexpected error: -95
1212
missed/kprobe_recursion # missed_kprobe_recursion__attach unexpected error: -95 (errno 95)
13+
verifier_arena # JIT does not support arena

tools/testing/selftests/bpf/DENYLIST.s390x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ exceptions # JIT does not support calling kfunc bpf_throw (excepti
44
get_stack_raw_tp # user_stack corrupted user stack (no backchain userspace)
55
stacktrace_build_id # compare_map_keys stackid_hmap vs. stackmap err -2 errno 2 (?)
66
verifier_iterating_callbacks
7+
verifier_arena # JIT does not support arena
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
2+
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
3+
#pragma once
4+
5+
#ifndef WRITE_ONCE
6+
#define WRITE_ONCE(x, val) ((*(volatile typeof(x) *) &(x)) = (val))
7+
#endif
8+
9+
#ifndef NUMA_NO_NODE
10+
#define NUMA_NO_NODE (-1)
11+
#endif
12+
13+
#ifndef arena_container_of
14+
#define arena_container_of(ptr, type, member) \
15+
({ \
16+
void __arena *__mptr = (void __arena *)(ptr); \
17+
((type *)(__mptr - offsetof(type, member))); \
18+
})
19+
#endif
20+
21+
#ifdef __BPF__ /* when compiled as bpf program */
22+
23+
#ifndef PAGE_SIZE
24+
#define PAGE_SIZE __PAGE_SIZE
25+
/*
26+
* for older kernels try sizeof(struct genradix_node)
27+
* or flexible:
28+
* static inline long __bpf_page_size(void) {
29+
* return bpf_core_enum_value(enum page_size_enum___l, __PAGE_SIZE___l) ?: sizeof(struct genradix_node);
30+
* }
31+
* but generated code is not great.
32+
*/
33+
#endif
34+
35+
#if defined(__BPF_FEATURE_ARENA_CAST) && !defined(BPF_ARENA_FORCE_ASM)
36+
#define __arena __attribute__((address_space(1)))
37+
#define cast_kern(ptr) /* nop for bpf prog. emitted by LLVM */
38+
#define cast_user(ptr) /* nop for bpf prog. emitted by LLVM */
39+
#else
40+
#define __arena
41+
#define cast_kern(ptr) bpf_addr_space_cast(ptr, 0, 1)
42+
#define cast_user(ptr) bpf_addr_space_cast(ptr, 1, 0)
43+
#endif
44+
45+
void __arena* bpf_arena_alloc_pages(void *map, void __arena *addr, __u32 page_cnt,
46+
int node_id, __u64 flags) __ksym __weak;
47+
void bpf_arena_free_pages(void *map, void __arena *ptr, __u32 page_cnt) __ksym __weak;
48+
49+
#else /* when compiled as user space code */
50+
51+
#define __arena
52+
#define __arg_arena
53+
#define cast_kern(ptr) /* nop for user space */
54+
#define cast_user(ptr) /* nop for user space */
55+
__weak char arena[1];
56+
57+
#ifndef offsetof
58+
#define offsetof(type, member) ((unsigned long)&((type *)0)->member)
59+
#endif
60+
61+
static inline void __arena* bpf_arena_alloc_pages(void *map, void *addr, __u32 page_cnt,
62+
int node_id, __u64 flags)
63+
{
64+
return NULL;
65+
}
66+
static inline void bpf_arena_free_pages(void *map, void __arena *ptr, __u32 page_cnt)
67+
{
68+
}
69+
70+
#endif

tools/testing/selftests/bpf/prog_tests/verifier.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "cap_helpers.h"
66
#include "verifier_and.skel.h"
7+
#include "verifier_arena.skel.h"
78
#include "verifier_array_access.skel.h"
89
#include "verifier_basic_stack.skel.h"
910
#include "verifier_bitfield_write.skel.h"
@@ -118,6 +119,7 @@ static void run_tests_aux(const char *skel_name,
118119
#define RUN(skel) run_tests_aux(#skel, skel##__elf_bytes, NULL)
119120

120121
void test_verifier_and(void) { RUN(verifier_and); }
122+
void test_verifier_arena(void) { RUN(verifier_arena); }
121123
void test_verifier_basic_stack(void) { RUN(verifier_basic_stack); }
122124
void test_verifier_bitfield_write(void) { RUN(verifier_bitfield_write); }
123125
void test_verifier_bounds(void) { RUN(verifier_bounds); }
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
3+
4+
#include <vmlinux.h>
5+
#include <bpf/bpf_helpers.h>
6+
#include <bpf/bpf_tracing.h>
7+
#include "bpf_misc.h"
8+
#include "bpf_experimental.h"
9+
#include "bpf_arena_common.h"
10+
11+
struct {
12+
__uint(type, BPF_MAP_TYPE_ARENA);
13+
__uint(map_flags, BPF_F_MMAPABLE);
14+
__uint(max_entries, 2); /* arena of two pages close to 32-bit boundary*/
15+
__ulong(map_extra, (1ull << 44) | (~0u - __PAGE_SIZE * 2 + 1)); /* start of mmap() region */
16+
} arena SEC(".maps");
17+
18+
SEC("syscall")
19+
__success __retval(0)
20+
int basic_alloc1(void *ctx)
21+
{
22+
#if defined(__BPF_FEATURE_ARENA_CAST)
23+
volatile int __arena *page1, *page2, *no_page, *page3;
24+
25+
page1 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
26+
if (!page1)
27+
return 1;
28+
*page1 = 1;
29+
page2 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
30+
if (!page2)
31+
return 2;
32+
*page2 = 2;
33+
no_page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
34+
if (no_page)
35+
return 3;
36+
if (*page1 != 1)
37+
return 4;
38+
if (*page2 != 2)
39+
return 5;
40+
bpf_arena_free_pages(&arena, (void __arena *)page2, 1);
41+
if (*page1 != 1)
42+
return 6;
43+
if (*page2 != 0) /* use-after-free should return 0 */
44+
return 7;
45+
page3 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
46+
if (!page3)
47+
return 8;
48+
*page3 = 3;
49+
if (page2 != page3)
50+
return 9;
51+
if (*page1 != 1)
52+
return 10;
53+
#endif
54+
return 0;
55+
}
56+
57+
SEC("syscall")
58+
__success __retval(0)
59+
int basic_alloc2(void *ctx)
60+
{
61+
#if defined(__BPF_FEATURE_ARENA_CAST)
62+
volatile char __arena *page1, *page2, *page3, *page4;
63+
64+
page1 = bpf_arena_alloc_pages(&arena, NULL, 2, NUMA_NO_NODE, 0);
65+
if (!page1)
66+
return 1;
67+
page2 = page1 + __PAGE_SIZE;
68+
page3 = page1 + __PAGE_SIZE * 2;
69+
page4 = page1 - __PAGE_SIZE;
70+
*page1 = 1;
71+
*page2 = 2;
72+
*page3 = 3;
73+
*page4 = 4;
74+
if (*page1 != 1)
75+
return 1;
76+
if (*page2 != 2)
77+
return 2;
78+
if (*page3 != 0)
79+
return 3;
80+
if (*page4 != 0)
81+
return 4;
82+
bpf_arena_free_pages(&arena, (void __arena *)page1, 2);
83+
if (*page1 != 0)
84+
return 5;
85+
if (*page2 != 0)
86+
return 6;
87+
if (*page3 != 0)
88+
return 7;
89+
if (*page4 != 0)
90+
return 8;
91+
#endif
92+
return 0;
93+
}
94+
95+
struct bpf_arena___l {
96+
struct bpf_map map;
97+
} __attribute__((preserve_access_index));
98+
99+
SEC("syscall")
100+
__success __retval(0) __log_level(2)
101+
int basic_alloc3(void *ctx)
102+
{
103+
struct bpf_arena___l *ar = (struct bpf_arena___l *)&arena;
104+
volatile char __arena *pages;
105+
106+
pages = bpf_arena_alloc_pages(&ar->map, NULL, ar->map.max_entries, NUMA_NO_NODE, 0);
107+
if (!pages)
108+
return 1;
109+
return 0;
110+
}
111+
112+
SEC("iter.s/bpf_map")
113+
__success __log_level(2)
114+
int iter_maps1(struct bpf_iter__bpf_map *ctx)
115+
{
116+
struct bpf_map *map = ctx->map;
117+
118+
if (!map)
119+
return 0;
120+
bpf_arena_alloc_pages(map, NULL, map->max_entries, 0, 0);
121+
return 0;
122+
}
123+
124+
SEC("iter.s/bpf_map")
125+
__failure __msg("expected pointer to STRUCT bpf_map")
126+
int iter_maps2(struct bpf_iter__bpf_map *ctx)
127+
{
128+
struct seq_file *seq = ctx->meta->seq;
129+
130+
bpf_arena_alloc_pages((void *)seq, NULL, 1, 0, 0);
131+
return 0;
132+
}
133+
134+
SEC("iter.s/bpf_map")
135+
__failure __msg("untrusted_ptr_bpf_map")
136+
int iter_maps3(struct bpf_iter__bpf_map *ctx)
137+
{
138+
struct bpf_map *map = ctx->map;
139+
140+
if (!map)
141+
return 0;
142+
bpf_arena_alloc_pages(map->inner_map_meta, NULL, map->max_entries, 0, 0);
143+
return 0;
144+
}
145+
146+
char _license[] SEC("license") = "GPL";

tools/testing/selftests/bpf/test_loader.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ static bool is_unpriv_capable_map(struct bpf_map *map)
501501
}
502502
}
503503

504-
static int do_prog_test_run(int fd_prog, int *retval)
504+
static int do_prog_test_run(int fd_prog, int *retval, bool empty_opts)
505505
{
506506
__u8 tmp_out[TEST_DATA_LEN << 2] = {};
507507
__u8 tmp_in[TEST_DATA_LEN] = {};
@@ -514,6 +514,10 @@ static int do_prog_test_run(int fd_prog, int *retval)
514514
.repeat = 1,
515515
);
516516

517+
if (empty_opts) {
518+
memset(&topts, 0, sizeof(struct bpf_test_run_opts));
519+
topts.sz = sizeof(struct bpf_test_run_opts);
520+
}
517521
err = bpf_prog_test_run_opts(fd_prog, &topts);
518522
saved_errno = errno;
519523

@@ -649,7 +653,8 @@ void run_subtest(struct test_loader *tester,
649653
}
650654
}
651655

652-
do_prog_test_run(bpf_program__fd(tprog), &retval);
656+
do_prog_test_run(bpf_program__fd(tprog), &retval,
657+
bpf_program__type(tprog) == BPF_PROG_TYPE_SYSCALL ? true : false);
653658
if (retval != subspec->retval && subspec->retval != POINTER_VALUE) {
654659
PRINT_FAIL("Unexpected retval: %d != %d\n", retval, subspec->retval);
655660
goto tobj_cleanup;

0 commit comments

Comments
 (0)