xref: /linux/tools/testing/selftests/bpf/progs/rcu_read_lock.c (revision 0a91336e287ca2557fead5221d2c79e0effd034e)
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