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") 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") 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") 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") 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") 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") 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") 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") 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") 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