1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3
4 #include <vmlinux.h>
5 #include <stdbool.h>
6 #include <bpf/bpf_helpers.h>
7 #include <bpf/bpf_tracing.h>
8 #include "bpf_misc.h"
9
10 #define ENOENT 2
11 #define EEXIST 17
12
13 char _license[] SEC("license") = "GPL";
14
15 int err;
16
17 struct elem {
18 char arr[128];
19 int val;
20 };
21
22 struct special_elem {
23 struct task_struct __kptr *task;
24 int val;
25 };
26
27 struct {
28 __uint(type, BPF_MAP_TYPE_RHASH);
29 __uint(map_flags, BPF_F_NO_PREALLOC);
30 __uint(max_entries, 128);
31 __type(key, int);
32 __type(value, struct elem);
33 } rhmap SEC(".maps");
34
35 struct {
36 __uint(type, BPF_MAP_TYPE_RHASH);
37 __uint(map_flags, BPF_F_NO_PREALLOC);
38 __uint(max_entries, 1);
39 __type(key, int);
40 __type(value, struct special_elem);
41 } special_fields SEC(".maps");
42
43 extern struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym;
44 extern void bpf_task_release(struct task_struct *p) __ksym;
45
46 SEC("syscall")
test_rhash_lookup_update(void * ctx)47 int test_rhash_lookup_update(void *ctx)
48 {
49 int key = 5;
50 struct elem empty = {.val = 3, .arr = {0}};
51 struct elem *e;
52
53 err = 1;
54 e = bpf_map_lookup_elem(&rhmap, &key);
55 if (e)
56 return 1;
57
58 err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
59 if (err)
60 return 1;
61
62 e = bpf_map_lookup_elem(&rhmap, &key);
63 if (!e || e->val != empty.val) {
64 err = 2;
65 return 2;
66 }
67
68 err = 0;
69 return 0;
70 }
71
72 SEC("syscall")
test_rhash_update_delete(void * ctx)73 int test_rhash_update_delete(void *ctx)
74 {
75 int key = 6;
76 struct elem empty = {.val = 4, .arr = {0}};
77 struct elem *e;
78
79 err = 1;
80 e = bpf_map_lookup_elem(&rhmap, &key);
81 if (e)
82 return 1;
83
84 err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
85 if (err)
86 return 2;
87
88 err = bpf_map_delete_elem(&rhmap, &key);
89 if (err)
90 return 3;
91
92 e = bpf_map_lookup_elem(&rhmap, &key);
93 if (e) {
94 err = 4;
95 return 4;
96 }
97
98 err = 0;
99 return 0;
100 }
101
102 SEC("syscall")
test_rhash_update_elements(void * ctx)103 int test_rhash_update_elements(void *ctx)
104 {
105 int key = 0;
106 struct elem empty = {.val = 4, .arr = {0}};
107 struct elem *e;
108 int i;
109
110 err = 1;
111
112 for (i = 0; i < 128; ++i) {
113 key = i;
114 e = bpf_map_lookup_elem(&rhmap, &key);
115 if (e)
116 return 1;
117
118 empty.val = key;
119 err = bpf_map_update_elem(&rhmap, &key, &empty, BPF_NOEXIST);
120 if (err)
121 return 2;
122
123 e = bpf_map_lookup_elem(&rhmap, &key);
124 if (!e || e->val != key) {
125 err = 4;
126 return 4;
127 }
128 }
129
130 for (i = 0; i < 128; ++i) {
131 key = i;
132 err = bpf_map_delete_elem(&rhmap, &key);
133 if (err)
134 return 3;
135
136 e = bpf_map_lookup_elem(&rhmap, &key);
137 if (e) {
138 err = 5;
139 return 5;
140 }
141 }
142
143 err = 0;
144 return 0;
145 }
146
147 SEC("syscall")
test_rhash_update_exist(void * ctx)148 int test_rhash_update_exist(void *ctx)
149 {
150 int key = 10;
151 struct elem val1 = {.val = 100, .arr = {0}};
152 struct elem val2 = {.val = 200, .arr = {0}};
153 struct elem *e;
154 int ret;
155
156 err = 1;
157
158 /* BPF_EXIST on non-existent key should fail with -ENOENT */
159 ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_EXIST);
160 if (ret != -ENOENT)
161 return 1;
162
163 /* Insert element first */
164 ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_NOEXIST);
165 if (ret)
166 return 2;
167
168 /* Verify initial value */
169 e = bpf_map_lookup_elem(&rhmap, &key);
170 if (!e || e->val != 100)
171 return 3;
172
173 /* BPF_EXIST on existing key should succeed and update value */
174 ret = bpf_map_update_elem(&rhmap, &key, &val2, BPF_EXIST);
175 if (ret)
176 return 4;
177
178 /* Verify value was updated */
179 e = bpf_map_lookup_elem(&rhmap, &key);
180 if (!e || e->val != 200)
181 return 5;
182
183 /* Cleanup */
184 bpf_map_delete_elem(&rhmap, &key);
185 err = 0;
186 return 0;
187 }
188
189 SEC("syscall")
test_rhash_update_any(void * ctx)190 int test_rhash_update_any(void *ctx)
191 {
192 int key = 11;
193 struct elem val1 = {.val = 111, .arr = {0}};
194 struct elem val2 = {.val = 222, .arr = {0}};
195 struct elem *e;
196 int ret;
197
198 err = 1;
199
200 /* BPF_ANY on non-existent key should insert */
201 ret = bpf_map_update_elem(&rhmap, &key, &val1, BPF_ANY);
202 if (ret)
203 return 1;
204
205 e = bpf_map_lookup_elem(&rhmap, &key);
206 if (!e || e->val != 111)
207 return 2;
208
209 /* BPF_ANY on existing key should update */
210 ret = bpf_map_update_elem(&rhmap, &key, &val2, BPF_ANY);
211 if (ret)
212 return 3;
213
214 e = bpf_map_lookup_elem(&rhmap, &key);
215 if (!e || e->val != 222)
216 return 4;
217
218 /* Cleanup */
219 bpf_map_delete_elem(&rhmap, &key);
220 err = 0;
221 return 0;
222 }
223
224 SEC("syscall")
test_rhash_noexist_duplicate(void * ctx)225 int test_rhash_noexist_duplicate(void *ctx)
226 {
227 int key = 12;
228 struct elem val = {.val = 600, .arr = {0}};
229 int ret;
230
231 err = 1;
232
233 /* Insert element */
234 ret = bpf_map_update_elem(&rhmap, &key, &val, BPF_NOEXIST);
235 if (ret)
236 return 1;
237
238 /* Try to insert again with BPF_NOEXIST - should fail with -EEXIST */
239 ret = bpf_map_update_elem(&rhmap, &key, &val, BPF_NOEXIST);
240 if (ret != -EEXIST)
241 return 2;
242
243 /* Cleanup */
244 bpf_map_delete_elem(&rhmap, &key);
245 err = 0;
246 return 0;
247 }
248
249 SEC("syscall")
test_rhash_delete_nonexistent(void * ctx)250 int test_rhash_delete_nonexistent(void *ctx)
251 {
252 int key = 99999;
253 int ret;
254
255 err = 1;
256
257 /* Delete non-existent key should return -ENOENT */
258 ret = bpf_map_delete_elem(&rhmap, &key);
259 if (ret != -ENOENT)
260 return 1;
261
262 err = 0;
263 return 0;
264 }
265
266 SEC("syscall")
test_rhash_kptr_update(void * ctx)267 int test_rhash_kptr_update(void *ctx)
268 {
269 struct special_elem val1 = { .val = 1 };
270 struct special_elem val2 = { .val = 2 };
271 struct task_struct *task, *old;
272 struct special_elem *elem;
273 int key = 0;
274
275 err = 1;
276 if (bpf_map_update_elem(&special_fields, &key, &val1, BPF_NOEXIST))
277 return 1;
278
279 err = 2;
280 elem = bpf_map_lookup_elem(&special_fields, &key);
281 if (!elem)
282 return 2;
283
284 err = 3;
285 task = bpf_task_acquire(bpf_get_current_task_btf());
286 if (!task)
287 return 3;
288
289 err = 4;
290 old = bpf_kptr_xchg(&elem->task, task);
291 if (old) {
292 bpf_task_release(old);
293 return 4;
294 }
295
296 err = 5;
297 if (bpf_map_update_elem(&special_fields, &key, &val2, BPF_EXIST))
298 return 5;
299
300 err = 6;
301 elem = bpf_map_lookup_elem(&special_fields, &key);
302 if (!elem || elem->val != 2)
303 return 6;
304
305 err = 7;
306 old = bpf_kptr_xchg(&elem->task, NULL);
307 if (!old)
308 return 7;
309 bpf_task_release(old);
310
311 err = 8;
312 if (bpf_map_delete_elem(&special_fields, &key))
313 return 8;
314
315 err = 0;
316 return 0;
317 }
318
319 SEC("syscall")
test_rhash_kptr_delete(void * ctx)320 int test_rhash_kptr_delete(void *ctx)
321 {
322 struct special_elem val = {};
323 struct task_struct *task, *old;
324 struct special_elem *elem;
325 int key = 0;
326
327 err = 1;
328 if (bpf_map_update_elem(&special_fields, &key, &val, BPF_NOEXIST))
329 return 1;
330
331 err = 2;
332 elem = bpf_map_lookup_elem(&special_fields, &key);
333 if (!elem)
334 return 2;
335
336 err = 3;
337 task = bpf_task_acquire(bpf_get_current_task_btf());
338 if (!task)
339 return 3;
340
341 err = 4;
342 old = bpf_kptr_xchg(&elem->task, task);
343 if (old) {
344 bpf_task_release(old);
345 return 4;
346 }
347
348 err = 5;
349 if (bpf_map_delete_elem(&special_fields, &key))
350 return 5;
351
352 err = 6;
353 old = bpf_kptr_xchg(&elem->task, NULL);
354 if (!old)
355 return 6;
356 bpf_task_release(old);
357
358 err = 0;
359 return 0;
360 }
361