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