1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022 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_tracing_net.h"
8 #include "bpf_misc.h"
9
10 char _license[] SEC("license") = "GPL";
11
12 struct {
13 __uint(type, BPF_MAP_TYPE_TASK_STORAGE);
14 __uint(map_flags, BPF_F_NO_PREALLOC);
15 __type(key, int);
16 __type(value, long);
17 } map_a SEC(".maps");
18
19 __u32 user_data, target_pid;
20 __s32 key_serial;
21 __u64 flags, task_storage_val, cgroup_id;
22
23 struct bpf_key *bpf_lookup_user_key(__s32 serial, __u64 flags) __ksym;
24 void bpf_key_put(struct bpf_key *key) __ksym;
25 void bpf_rcu_read_lock(void) __ksym;
26 void bpf_rcu_read_unlock(void) __ksym;
27 struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym;
28 void bpf_task_release(struct task_struct *p) __ksym;
29
30 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
get_cgroup_id(void * ctx)31 int get_cgroup_id(void *ctx)
32 {
33 struct task_struct *task;
34 struct css_set *cgroups;
35
36 task = bpf_get_current_task_btf();
37 if (task->pid != target_pid)
38 return 0;
39
40 /* simulate bpf_get_current_cgroup_id() helper */
41 bpf_rcu_read_lock();
42 cgroups = task->cgroups;
43 if (!cgroups)
44 goto unlock;
45 cgroup_id = cgroups->dfl_cgrp->kn->id;
46 unlock:
47 bpf_rcu_read_unlock();
48 return 0;
49 }
50
51 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
task_succ(void * ctx)52 int task_succ(void *ctx)
53 {
54 struct task_struct *task, *real_parent;
55 long init_val = 2;
56 long *ptr;
57
58 task = bpf_get_current_task_btf();
59 if (task->pid != target_pid)
60 return 0;
61
62 bpf_rcu_read_lock();
63 /* region including helper using rcu ptr real_parent */
64 real_parent = task->real_parent;
65 if (!real_parent)
66 goto out;
67 ptr = bpf_task_storage_get(&map_a, real_parent, &init_val,
68 BPF_LOCAL_STORAGE_GET_F_CREATE);
69 if (!ptr)
70 goto out;
71 ptr = bpf_task_storage_get(&map_a, real_parent, 0, 0);
72 if (!ptr)
73 goto out;
74 task_storage_val = *ptr;
75 out:
76 bpf_rcu_read_unlock();
77 return 0;
78 }
79
80 SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep")
no_lock(void * ctx)81 int no_lock(void *ctx)
82 {
83 struct task_struct *task, *real_parent;
84
85 /* old style ptr_to_btf_id is not allowed in sleepable */
86 task = bpf_get_current_task_btf();
87 real_parent = task->real_parent;
88 (void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
89 return 0;
90 }
91
92 SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep")
two_regions(void * ctx)93 int two_regions(void *ctx)
94 {
95 struct task_struct *task, *real_parent;
96
97 /* two regions */
98 task = bpf_get_current_task_btf();
99 bpf_rcu_read_lock();
100 bpf_rcu_read_unlock();
101 bpf_rcu_read_lock();
102 real_parent = task->real_parent;
103 if (!real_parent)
104 goto out;
105 (void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
106 out:
107 bpf_rcu_read_unlock();
108 return 0;
109 }
110
111 SEC("?fentry/" SYS_PREFIX "sys_getpgid")
non_sleepable_1(void * ctx)112 int non_sleepable_1(void *ctx)
113 {
114 struct task_struct *task, *real_parent;
115
116 task = bpf_get_current_task_btf();
117 bpf_rcu_read_lock();
118 real_parent = task->real_parent;
119 if (!real_parent)
120 goto out;
121 (void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
122 out:
123 bpf_rcu_read_unlock();
124 return 0;
125 }
126
127 SEC("?fentry/" SYS_PREFIX "sys_getpgid")
non_sleepable_2(void * ctx)128 int non_sleepable_2(void *ctx)
129 {
130 struct task_struct *task, *real_parent;
131
132 bpf_rcu_read_lock();
133 task = bpf_get_current_task_btf();
134 bpf_rcu_read_unlock();
135
136 bpf_rcu_read_lock();
137 real_parent = task->real_parent;
138 if (!real_parent)
139 goto out;
140 (void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
141 out:
142 bpf_rcu_read_unlock();
143 return 0;
144 }
145
146 SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep")
task_acquire(void * ctx)147 int task_acquire(void *ctx)
148 {
149 struct task_struct *task, *real_parent, *gparent;
150
151 task = bpf_get_current_task_btf();
152 bpf_rcu_read_lock();
153 real_parent = task->real_parent;
154 if (!real_parent)
155 goto out;
156
157 /* rcu_ptr->rcu_field */
158 gparent = real_parent->real_parent;
159 if (!gparent)
160 goto out;
161
162 /* acquire a reference which can be used outside rcu read lock region */
163 gparent = bpf_task_acquire(gparent);
164 if (!gparent)
165 goto out;
166
167 (void)bpf_task_storage_get(&map_a, gparent, 0, 0);
168 bpf_task_release(gparent);
169 out:
170 bpf_rcu_read_unlock();
171 return 0;
172 }
173
174 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
miss_lock(void * ctx)175 int miss_lock(void *ctx)
176 {
177 struct task_struct *task;
178
179 /* missing bpf_rcu_read_lock() */
180 task = bpf_get_current_task_btf();
181 bpf_rcu_read_lock();
182 (void)bpf_task_storage_get(&map_a, task, 0, 0);
183 bpf_rcu_read_unlock();
184 bpf_rcu_read_unlock();
185 return 0;
186 }
187
188 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
miss_unlock(void * ctx)189 int miss_unlock(void *ctx)
190 {
191 struct task_struct *task;
192
193 /* missing bpf_rcu_read_unlock() */
194 task = bpf_get_current_task_btf();
195 bpf_rcu_read_lock();
196 (void)bpf_task_storage_get(&map_a, task, 0, 0);
197 return 0;
198 }
199
200 SEC("?fentry/" SYS_PREFIX "sys_getpgid")
non_sleepable_rcu_mismatch(void * ctx)201 int non_sleepable_rcu_mismatch(void *ctx)
202 {
203 struct task_struct *task, *real_parent;
204
205 task = bpf_get_current_task_btf();
206 /* non-sleepable: missing bpf_rcu_read_unlock() in one path */
207 bpf_rcu_read_lock();
208 real_parent = task->real_parent;
209 if (!real_parent)
210 goto out;
211 (void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
212 if (real_parent)
213 bpf_rcu_read_unlock();
214 out:
215 return 0;
216 }
217
218 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
inproper_sleepable_helper(void * ctx)219 int inproper_sleepable_helper(void *ctx)
220 {
221 struct task_struct *task, *real_parent;
222 struct pt_regs *regs;
223 __u32 value = 0;
224 void *ptr;
225
226 task = bpf_get_current_task_btf();
227 /* sleepable helper in rcu read lock region */
228 bpf_rcu_read_lock();
229 real_parent = task->real_parent;
230 if (!real_parent)
231 goto out;
232 regs = (struct pt_regs *)bpf_task_pt_regs(real_parent);
233 if (!regs)
234 goto out;
235
236 ptr = (void *)PT_REGS_IP(regs);
237 (void)bpf_copy_from_user_task(&value, sizeof(uint32_t), ptr, task, 0);
238 user_data = value;
239 (void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
240 out:
241 bpf_rcu_read_unlock();
242 return 0;
243 }
244
245 SEC("?lsm.s/bpf")
BPF_PROG(inproper_sleepable_kfunc,int cmd,union bpf_attr * attr,unsigned int size,bool kernel)246 int BPF_PROG(inproper_sleepable_kfunc, int cmd, union bpf_attr *attr, unsigned int size,
247 bool kernel)
248 {
249 struct bpf_key *bkey;
250
251 /* sleepable kfunc in rcu read lock region */
252 bpf_rcu_read_lock();
253 bkey = bpf_lookup_user_key(key_serial, flags);
254 bpf_rcu_read_unlock();
255 if (!bkey)
256 return -1;
257 bpf_key_put(bkey);
258
259 return 0;
260 }
261
262 SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep")
nested_rcu_region(void * ctx)263 int nested_rcu_region(void *ctx)
264 {
265 struct task_struct *task, *real_parent;
266
267 /* nested rcu read lock regions */
268 task = bpf_get_current_task_btf();
269 bpf_rcu_read_lock();
270 bpf_rcu_read_lock();
271 real_parent = task->real_parent;
272 if (!real_parent)
273 goto out;
274 (void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
275 out:
276 bpf_rcu_read_unlock();
277 bpf_rcu_read_unlock();
278 return 0;
279 }
280
281 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
task_trusted_non_rcuptr(void * ctx)282 int task_trusted_non_rcuptr(void *ctx)
283 {
284 struct task_struct *task, *group_leader;
285
286 task = bpf_get_current_task_btf();
287 bpf_rcu_read_lock();
288 /* the pointer group_leader is explicitly marked as trusted */
289 group_leader = task->real_parent->group_leader;
290 (void)bpf_task_storage_get(&map_a, group_leader, 0, 0);
291 bpf_rcu_read_unlock();
292 return 0;
293 }
294
295 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
task_untrusted_rcuptr(void * ctx)296 int task_untrusted_rcuptr(void *ctx)
297 {
298 struct task_struct *task, *real_parent;
299
300 task = bpf_get_current_task_btf();
301 bpf_rcu_read_lock();
302 real_parent = task->real_parent;
303 bpf_rcu_read_unlock();
304 /* helper use of rcu ptr outside the rcu read lock region */
305 (void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
306 return 0;
307 }
308
309 SEC("?fentry.s/" SYS_PREFIX "sys_nanosleep")
cross_rcu_region(void * ctx)310 int cross_rcu_region(void *ctx)
311 {
312 struct task_struct *task, *real_parent;
313
314 /* rcu ptr define/use in different regions */
315 task = bpf_get_current_task_btf();
316 bpf_rcu_read_lock();
317 real_parent = task->real_parent;
318 bpf_rcu_read_unlock();
319 bpf_rcu_read_lock();
320 (void)bpf_task_storage_get(&map_a, real_parent, 0, 0);
321 bpf_rcu_read_unlock();
322 return 0;
323 }
324
325 __noinline
static_subprog(void * ctx)326 static int static_subprog(void *ctx)
327 {
328 volatile int ret = 0;
329
330 if (bpf_get_prandom_u32())
331 return ret + 42;
332 return ret + bpf_get_prandom_u32();
333 }
334
335 __noinline
global_subprog(u64 a)336 int global_subprog(u64 a)
337 {
338 volatile int ret = a;
339
340 return ret + static_subprog(NULL);
341 }
342
343 __noinline
static_subprog_lock(void * ctx)344 static int static_subprog_lock(void *ctx)
345 {
346 volatile int ret = 0;
347
348 bpf_rcu_read_lock();
349 if (bpf_get_prandom_u32())
350 return ret + 42;
351 return ret + bpf_get_prandom_u32();
352 }
353
354 __noinline
global_subprog_lock(u64 a)355 int global_subprog_lock(u64 a)
356 {
357 volatile int ret = a;
358
359 return ret + static_subprog_lock(NULL);
360 }
361
362 __noinline
static_subprog_unlock(void * ctx)363 static int static_subprog_unlock(void *ctx)
364 {
365 volatile int ret = 0;
366
367 bpf_rcu_read_unlock();
368 if (bpf_get_prandom_u32())
369 return ret + 42;
370 return ret + bpf_get_prandom_u32();
371 }
372
373 __noinline
global_subprog_unlock(u64 a)374 int global_subprog_unlock(u64 a)
375 {
376 volatile int ret = a;
377
378 return ret + static_subprog_unlock(NULL);
379 }
380
381 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
rcu_read_lock_subprog(void * ctx)382 int rcu_read_lock_subprog(void *ctx)
383 {
384 volatile int ret = 0;
385
386 bpf_rcu_read_lock();
387 if (bpf_get_prandom_u32())
388 ret += static_subprog(ctx);
389 bpf_rcu_read_unlock();
390 return 0;
391 }
392
393 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
rcu_read_lock_global_subprog(void * ctx)394 int rcu_read_lock_global_subprog(void *ctx)
395 {
396 volatile int ret = 0;
397
398 bpf_rcu_read_lock();
399 if (bpf_get_prandom_u32())
400 ret += global_subprog(ret);
401 bpf_rcu_read_unlock();
402 return 0;
403 }
404
405 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
rcu_read_lock_subprog_lock(void * ctx)406 int rcu_read_lock_subprog_lock(void *ctx)
407 {
408 volatile int ret = 0;
409
410 ret += static_subprog_lock(ctx);
411 bpf_rcu_read_unlock();
412 return 0;
413 }
414
415 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
rcu_read_lock_global_subprog_lock(void * ctx)416 int rcu_read_lock_global_subprog_lock(void *ctx)
417 {
418 volatile int ret = 0;
419
420 ret += global_subprog_lock(ret);
421 bpf_rcu_read_unlock();
422 return 0;
423 }
424
425 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
rcu_read_lock_subprog_unlock(void * ctx)426 int rcu_read_lock_subprog_unlock(void *ctx)
427 {
428 volatile int ret = 0;
429
430 bpf_rcu_read_lock();
431 ret += static_subprog_unlock(ctx);
432 return 0;
433 }
434
435 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
rcu_read_lock_global_subprog_unlock(void * ctx)436 int rcu_read_lock_global_subprog_unlock(void *ctx)
437 {
438 volatile int ret = 0;
439
440 bpf_rcu_read_lock();
441 ret += global_subprog_unlock(ret);
442 return 0;
443 }
444
445 int __noinline
global_sleepable_helper_subprog(int i)446 global_sleepable_helper_subprog(int i)
447 {
448 if (i)
449 bpf_copy_from_user(&i, sizeof(i), NULL);
450 return i;
451 }
452
453 int __noinline
global_sleepable_kfunc_subprog(int i)454 global_sleepable_kfunc_subprog(int i)
455 {
456 if (i)
457 bpf_copy_from_user_str(&i, sizeof(i), NULL, 0);
458 global_subprog(i);
459 return i;
460 }
461
462 int __noinline
global_subprog_calling_sleepable_global(int i)463 global_subprog_calling_sleepable_global(int i)
464 {
465 if (!i)
466 global_sleepable_kfunc_subprog(i);
467 return i;
468 }
469
470 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
rcu_read_lock_sleepable_helper_global_subprog(void * ctx)471 int rcu_read_lock_sleepable_helper_global_subprog(void *ctx)
472 {
473 volatile int ret = 0;
474
475 bpf_rcu_read_lock();
476 ret += global_sleepable_helper_subprog(ret);
477 bpf_rcu_read_unlock();
478 return 0;
479 }
480
481 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
rcu_read_lock_sleepable_kfunc_global_subprog(void * ctx)482 int rcu_read_lock_sleepable_kfunc_global_subprog(void *ctx)
483 {
484 volatile int ret = 0;
485
486 bpf_rcu_read_lock();
487 ret += global_sleepable_kfunc_subprog(ret);
488 bpf_rcu_read_unlock();
489 return 0;
490 }
491
492 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
rcu_read_lock_sleepable_global_subprog_indirect(void * ctx)493 int rcu_read_lock_sleepable_global_subprog_indirect(void *ctx)
494 {
495 volatile int ret = 0;
496
497 bpf_rcu_read_lock();
498 ret += global_subprog_calling_sleepable_global(ret);
499 bpf_rcu_read_unlock();
500 return 0;
501 }
502