1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2025 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 10 char _license[] SEC("license") = "GPL"; 11 12 struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym; 13 void bpf_task_release(struct task_struct *p) __ksym; 14 void bpf_rcu_read_lock(void) __ksym; 15 void bpf_rcu_read_unlock(void) __ksym; 16 17 /* Timer tests */ 18 19 struct timer_elem { 20 struct bpf_timer t; 21 }; 22 23 struct { 24 __uint(type, BPF_MAP_TYPE_ARRAY); 25 __uint(max_entries, 1); 26 __type(key, int); 27 __type(value, struct timer_elem); 28 } timer_map SEC(".maps"); 29 30 static int timer_cb(void *map, int *key, struct bpf_timer *timer) 31 { 32 u32 data; 33 /* Timer callbacks are never sleepable, even from non-sleepable programs */ 34 bpf_copy_from_user(&data, sizeof(data), NULL); 35 return 0; 36 } 37 38 SEC("fentry/bpf_fentry_test1") 39 __failure __msg("sleepable helper bpf_copy_from_user#{{[0-9]+}} in non-sleepable prog") 40 int timer_non_sleepable_prog(void *ctx) 41 { 42 struct timer_elem *val; 43 int key = 0; 44 45 val = bpf_map_lookup_elem(&timer_map, &key); 46 if (!val) 47 return 0; 48 49 bpf_timer_init(&val->t, &timer_map, 0); 50 bpf_timer_set_callback(&val->t, timer_cb); 51 return 0; 52 } 53 54 SEC("lsm.s/file_open") 55 __failure __msg("sleepable helper bpf_copy_from_user#{{[0-9]+}} in non-sleepable prog") 56 int timer_sleepable_prog(void *ctx) 57 { 58 struct timer_elem *val; 59 int key = 0; 60 61 val = bpf_map_lookup_elem(&timer_map, &key); 62 if (!val) 63 return 0; 64 65 bpf_timer_init(&val->t, &timer_map, 0); 66 bpf_timer_set_callback(&val->t, timer_cb); 67 return 0; 68 } 69 70 static int timer_sys_bpf_cb(void *map, int *key, struct bpf_timer *timer) 71 { 72 __u64 attr = 0; 73 74 bpf_sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr)); 75 return 0; 76 } 77 78 SEC("syscall") 79 __failure __msg("sleepable helper bpf_sys_bpf#{{[0-9]+}} in non-sleepable prog") 80 int timer_sys_bpf_prog(void *ctx) 81 { 82 struct timer_elem *val; 83 int key = 0; 84 85 val = bpf_map_lookup_elem(&timer_map, &key); 86 if (!val) 87 return 0; 88 89 bpf_timer_init(&val->t, &timer_map, 0); 90 bpf_timer_set_callback(&val->t, timer_sys_bpf_cb); 91 return 0; 92 } 93 94 static int timer_sys_close_cb(void *map, int *key, struct bpf_timer *timer) 95 { 96 bpf_sys_close(0); 97 return 0; 98 } 99 100 SEC("syscall") 101 __failure __msg("sleepable helper bpf_sys_close#{{[0-9]+}} in non-sleepable prog") 102 int timer_sys_close_prog(void *ctx) 103 { 104 struct timer_elem *val; 105 int key = 0; 106 107 val = bpf_map_lookup_elem(&timer_map, &key); 108 if (!val) 109 return 0; 110 111 bpf_timer_init(&val->t, &timer_map, 0); 112 bpf_timer_set_callback(&val->t, timer_sys_close_cb); 113 return 0; 114 } 115 116 static int timer_btf_find_cb(void *map, int *key, struct bpf_timer *timer) 117 { 118 char name[] = "task_struct"; 119 120 bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0); 121 return 0; 122 } 123 124 SEC("syscall") 125 __failure __msg("sleepable helper bpf_btf_find_by_name_kind#{{[0-9]+}} in non-sleepable prog") 126 int timer_btf_find_prog(void *ctx) 127 { 128 struct timer_elem *val; 129 int key = 0; 130 131 val = bpf_map_lookup_elem(&timer_map, &key); 132 if (!val) 133 return 0; 134 135 bpf_timer_init(&val->t, &timer_map, 0); 136 bpf_timer_set_callback(&val->t, timer_btf_find_cb); 137 return 0; 138 } 139 140 SEC("syscall") 141 __success 142 int syscall_sys_bpf_prog(void *ctx) 143 { 144 __u64 attr = 0; 145 146 bpf_sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr)); 147 return 0; 148 } 149 150 SEC("syscall") 151 __success 152 int syscall_sys_close_prog(void *ctx) 153 { 154 bpf_sys_close(0); 155 return 0; 156 } 157 158 SEC("syscall") 159 __success 160 int syscall_btf_find_prog(void *ctx) 161 { 162 char name[] = "task_struct"; 163 164 bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0); 165 return 0; 166 } 167 168 /* Workqueue tests */ 169 170 struct wq_elem { 171 struct bpf_wq w; 172 struct task_struct __kptr *task; 173 }; 174 175 struct { 176 __uint(type, BPF_MAP_TYPE_ARRAY); 177 __uint(max_entries, 1); 178 __type(key, int); 179 __type(value, struct wq_elem); 180 } wq_map SEC(".maps"); 181 182 static int wq_cb(void *map, int *key, void *value) 183 { 184 u32 data; 185 /* Workqueue callbacks are always sleepable, even from non-sleepable programs */ 186 bpf_copy_from_user(&data, sizeof(data), NULL); 187 return 0; 188 } 189 190 SEC("fentry/bpf_fentry_test1") 191 __success 192 int wq_non_sleepable_prog(void *ctx) 193 { 194 struct wq_elem *val; 195 int key = 0; 196 197 val = bpf_map_lookup_elem(&wq_map, &key); 198 if (!val) 199 return 0; 200 201 if (bpf_wq_init(&val->w, &wq_map, 0) != 0) 202 return 0; 203 if (bpf_wq_set_callback(&val->w, wq_cb, 0) != 0) 204 return 0; 205 return 0; 206 } 207 208 SEC("lsm.s/file_open") 209 __success 210 int wq_sleepable_prog(void *ctx) 211 { 212 struct wq_elem *val; 213 int key = 0; 214 215 val = bpf_map_lookup_elem(&wq_map, &key); 216 if (!val) 217 return 0; 218 219 if (bpf_wq_init(&val->w, &wq_map, 0) != 0) 220 return 0; 221 if (bpf_wq_set_callback(&val->w, wq_cb, 0) != 0) 222 return 0; 223 return 0; 224 } 225 226 __noinline int wq_global_acquire(void) 227 { 228 struct task_struct *task, *acquired; 229 struct wq_elem *val; 230 int key = 0; 231 232 val = bpf_map_lookup_elem(&wq_map, &key); 233 if (!val) 234 return 0; 235 236 task = val->task; 237 if (!task) 238 return 0; 239 240 acquired = bpf_task_acquire(task); 241 if (acquired) 242 bpf_task_release(acquired); 243 return 0; 244 } 245 246 static int wq_global_rcu_cb(void *map, int *key, void *value) 247 { 248 wq_global_acquire(); 249 return 0; 250 } 251 252 SEC("fentry/bpf_fentry_test1") 253 __failure __msg("R1 must be a rcu pointer") 254 int wq_global_rcu_prog(void *ctx) 255 { 256 struct wq_elem *val; 257 int key = 0; 258 259 val = bpf_map_lookup_elem(&wq_map, &key); 260 if (!val) 261 return 0; 262 263 bpf_wq_init(&val->w, &wq_map, 0); 264 bpf_wq_set_callback(&val->w, wq_global_rcu_cb, 0); 265 return 0; 266 } 267 268 static int wq_global_rcu_lock_cb(void *map, int *key, void *value) 269 { 270 bpf_rcu_read_lock(); 271 wq_global_acquire(); 272 bpf_rcu_read_unlock(); 273 return 0; 274 } 275 276 SEC("fentry/bpf_fentry_test1") 277 __success 278 int wq_global_rcu_lock_prog(void *ctx) 279 { 280 struct wq_elem *val; 281 int key = 0; 282 283 /* Verify the same global subprog in non-sleepable and protected contexts. */ 284 wq_global_acquire(); 285 286 val = bpf_map_lookup_elem(&wq_map, &key); 287 if (!val) 288 return 0; 289 290 bpf_wq_init(&val->w, &wq_map, 0); 291 bpf_wq_set_callback(&val->w, wq_global_rcu_lock_cb, 0); 292 return 0; 293 } 294 295 __weak __noinline int wq_global_no_rcu(void) 296 { 297 return 0; 298 } 299 300 static int wq_global_no_rcu_cb(void *map, int *key, void *value) 301 { 302 wq_global_no_rcu(); 303 return 0; 304 } 305 306 SEC("fentry/bpf_fentry_test1") 307 __success __log_level(4) 308 __msg("subprog {{[0-9]+}} (wq_global_no_rcu) global insns_self 4 insns_total 4 stack 0") 309 int wq_global_no_rcu_prog(void *ctx) 310 { 311 struct wq_elem *val; 312 int key = 0; 313 314 /* Verify the same global in non-sleepable and unprotected contexts. */ 315 wq_global_no_rcu(); 316 317 val = bpf_map_lookup_elem(&wq_map, &key); 318 if (!val) 319 return 0; 320 321 bpf_wq_init(&val->w, &wq_map, 0); 322 bpf_wq_set_callback(&val->w, wq_global_no_rcu_cb, 0); 323 return 0; 324 } 325 326 /* Task work tests */ 327 328 struct task_work_elem { 329 struct bpf_task_work tw; 330 }; 331 332 struct { 333 __uint(type, BPF_MAP_TYPE_ARRAY); 334 __uint(max_entries, 1); 335 __type(key, int); 336 __type(value, struct task_work_elem); 337 } task_work_map SEC(".maps"); 338 339 static int task_work_cb(struct bpf_map *map, void *key, void *value) 340 { 341 u32 data; 342 /* Task work callbacks are always sleepable, even from non-sleepable programs */ 343 bpf_copy_from_user(&data, sizeof(data), NULL); 344 return 0; 345 } 346 347 SEC("fentry/bpf_fentry_test1") 348 __success 349 int task_work_non_sleepable_prog(void *ctx) 350 { 351 struct task_work_elem *val; 352 struct task_struct *task; 353 int key = 0; 354 355 val = bpf_map_lookup_elem(&task_work_map, &key); 356 if (!val) 357 return 0; 358 359 task = bpf_get_current_task_btf(); 360 if (!task) 361 return 0; 362 363 bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb); 364 return 0; 365 } 366 367 SEC("lsm.s/file_open") 368 __success 369 int task_work_sleepable_prog(void *ctx) 370 { 371 struct task_work_elem *val; 372 struct task_struct *task; 373 int key = 0; 374 375 val = bpf_map_lookup_elem(&task_work_map, &key); 376 if (!val) 377 return 0; 378 379 task = bpf_get_current_task_btf(); 380 if (!task) 381 return 0; 382 383 bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb); 384 return 0; 385 } 386