1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3
4 #define BPF_NO_KFUNC_PROTOTYPES
5 #include <vmlinux.h>
6 #include <bpf/bpf_helpers.h>
7 #include "bpf_misc.h"
8 #include "bpf_experimental.h"
9 #include <bpf_arena_common.h>
10 #include "../test_kmods/bpf_testmod_kfunc.h"
11
12 struct {
13 __uint(type, BPF_MAP_TYPE_ARENA);
14 __uint(map_flags, BPF_F_MMAPABLE);
15 /* page 0 hosts the arena global, page 1 is for allocations */
16 __uint(max_entries, 2);
17 } arena SEC(".maps");
18
19 /*
20 * Occupies page 0 so no allocation lands at arena offset 0, which the
21 * nullable tests below must be able to tell apart from NULL.
22 */
23 u64 __arena arena_pad;
24
25 /* volatile to force the scalar reloads below */
26 volatile u64 stash;
27
28 SEC("syscall")
29 __arch_x86_64
30 __arch_arm64
31 __success __retval(0)
arena_arg_forms(void * ctx)32 int arena_arg_forms(void *ctx)
33 {
34 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
35 u64 __arena *val;
36 u64 ret;
37
38 val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
39 if (!val)
40 return 1;
41
42 /* PTR_TO_ARENA argument */
43 *val = 41;
44 ret = bpf_kfunc_arena_arg_test((u64 *)val);
45 if (ret != 41 || *val != 42)
46 return 2;
47
48 /* the low 32 bits as a scalar */
49 stash = (u32)(u64)val;
50 ret = bpf_kfunc_arena_arg_test((u64 *)stash);
51 if (ret != 42 || *val != 43)
52 return 3;
53
54 /* the full user address as a scalar */
55 stash = (u64)val;
56 bpf_addr_space_cast(stash, 1, 0);
57 ret = bpf_kfunc_arena_arg_test((u64 *)stash);
58 if (ret != 43 || *val != 44)
59 return 4;
60
61 bpf_arena_free_pages(&arena, (void __arena *)val, 1);
62 #endif
63 return 0;
64 }
65
66 /*
67 * Pin the rebase semantics using the capture kfuncs, which return the raw
68 * argument value: __arena rebases unconditionally, so zero low 32 bits
69 * arrive as the arena kernel base, while __arena__nullable turns them into
70 * NULL.
71 */
72 SEC("syscall")
73 __arch_x86_64
74 __arch_arm64
75 __success __retval(0)
arena_arg_rebase(void * ctx)76 int arena_arg_rebase(void *ctx)
77 {
78 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
79 u64 __arena *val;
80 u64 base, off;
81
82 val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
83 if (!val)
84 return 1;
85
86 base = bpf_kfunc_arena_cap_test(NULL);
87 if (!base)
88 return 2;
89
90 /* only the low 32 bits contribute */
91 stash = 0xbadc0ffe00000000;
92 if (bpf_kfunc_arena_cap_test((u64 *)stash) != base)
93 return 3;
94
95 off = (u32)(u64)val;
96 if (bpf_kfunc_arena_cap_test((u64 *)val) != base + off)
97 return 4;
98
99 if (bpf_kfunc_arena_cap_nullable_test(NULL) != 0)
100 return 5;
101
102 stash = 0xbadc0ffe00000000;
103 if (bpf_kfunc_arena_cap_nullable_test((u64 *)stash) != 0)
104 return 6;
105
106 if (bpf_kfunc_arena_cap_nullable_test((u64 *)val) != base + off)
107 return 7;
108
109 bpf_arena_free_pages(&arena, (void __arena *)val, 1);
110 #endif
111 return 0;
112 }
113
114 SEC("syscall")
115 __arch_x86_64
116 __arch_arm64
117 __success __retval(0)
arena_args5(void * ctx)118 int arena_args5(void *ctx)
119 {
120 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
121 u64 __arena *val;
122
123 val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
124 if (!val)
125 return 1;
126
127 val[0] = 1;
128 val[1] = 2;
129 val[2] = 4;
130 val[3] = 8;
131 val[4] = 16;
132
133 if (bpf_kfunc_arena_args5_test((u64 *)&val[0], (u64 *)&val[1],
134 (u64 *)&val[2], (u64 *)&val[3],
135 (u64 *)&val[4]) != 31)
136 return 2;
137 if (bpf_kfunc_arena_args5_test((u64 *)&val[0], (u64 *)&val[1],
138 (u64 *)&val[2], (u64 *)&val[3], NULL) != 15)
139 return 3;
140
141 bpf_arena_free_pages(&arena, (void __arena *)val, 1);
142 #endif
143 return 0;
144 }
145
146 SEC("syscall")
147 __arch_x86_64
148 __arch_arm64
149 __success __retval(0)
arena_arg_mixed(void * ctx)150 int arena_arg_mixed(void *ctx)
151 {
152 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
153 u64 __arena *val;
154
155 val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
156 if (!val)
157 return 1;
158
159 val[0] = 7;
160 val[1] = 5;
161
162 if (bpf_kfunc_arena_mixed_test((u64 *)&val[0], NULL) != 7)
163 return 2;
164
165 if (bpf_kfunc_arena_mixed_test((u64 *)&val[0], (u64 *)&val[1]) != 12)
166 return 3;
167
168 bpf_arena_free_pages(&arena, (void __arena *)val, 1);
169 #endif
170 return 0;
171 }
172
173 /* kernel-side faults on unpopulated pages recover via the scratch page */
174 SEC("syscall")
175 __arch_x86_64
176 __arch_arm64
177 __success __retval(0)
arena_arg_unpopulated(void * ctx)178 int arena_arg_unpopulated(void *ctx)
179 {
180 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
181 u64 __arena *val;
182
183 val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
184 if (!val)
185 return 1;
186
187 stash = (u64)val + PAGE_SIZE;
188 bpf_kfunc_arena_arg_test((u64 *)stash);
189
190 bpf_arena_free_pages(&arena, (void __arena *)val, 1);
191 #endif
192 return 0;
193 }
194
195 SEC("syscall")
196 __arch_x86_64
197 __arch_arm64
198 __failure __msg("arena pointer requires a program with an associated arena")
arena_arg_no_arena(void * ctx)199 int arena_arg_no_arena(void *ctx)
200 {
201 bpf_kfunc_arena_arg_test((u64 *)1);
202 return 0;
203 }
204
205 SEC("syscall")
206 __arch_x86_64
207 __arch_arm64
208 __failure __msg("is not a pointer to arena or scalar")
arena_arg_bad_reg(void * ctx)209 int arena_arg_bad_reg(void *ctx)
210 {
211 u64 buf = 0;
212
213 /* use the arena so the program passes the arena presence check */
214 bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
215 bpf_kfunc_arena_arg_test(&buf);
216 return 0;
217 }
218
219 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) && \
220 defined(__BPF_FEATURE_STACK_ARGUMENT)
221 SEC("syscall")
222 __arch_x86_64
223 __arch_arm64
224 __failure __msg("arena pointer cannot be a stack argument")
arena_arg_stack(void * ctx)225 int arena_arg_stack(void *ctx)
226 {
227 bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
228 bpf_kfunc_arena_stack_arg_test(1, 2, 3, 4, 5, (u64 *)1);
229 return 0;
230 }
231 #else
232 SEC("syscall")
233 __arch_x86_64
234 __arch_arm64
235 __description("arena_arg_stack: not supported, dummy test")
236 __success
arena_arg_stack(void * ctx)237 int arena_arg_stack(void *ctx)
238 {
239 return 0;
240 }
241 #endif
242
243 char _license[] SEC("license") = "GPL";
244