xref: /linux/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c (revision de36adca634634c205a9eb8b56a28175ab7abf5f)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2023 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 "xdp_metadata.h"
9 #include "bpf_kfuncs.h"
10 #include "err.h"
11 
12 /* The compiler may be able to detect the access to uninitialized
13    memory in the routines performing out of bound memory accesses and
14    emit warnings about it.  This is the case of GCC. */
15 #if !defined(__clang__)
16 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
17 #endif
18 
19 int arr[1];
20 int unkn_idx;
21 const volatile bool call_dead_subprog = false;
22 
23 __noinline long global_bad(void)
24 {
25 	return arr[unkn_idx]; /* BOOM */
26 }
27 
28 __noinline long global_good(void)
29 {
30 	return arr[0];
31 }
32 
33 __noinline long global_calls_bad(void)
34 {
35 	return global_good() + global_bad() /* does BOOM indirectly */;
36 }
37 
38 __noinline long global_calls_good_only(void)
39 {
40 	return global_good();
41 }
42 
43 __noinline long global_dead(void)
44 {
45 	return arr[0] * 2;
46 }
47 
48 SEC("?raw_tp")
49 __success __log_level(6)
50 /* main prog is validated completely first */
51 __msg("('global_calls_good_only') is global and assumed valid.")
52 /* eventually global_good() is transitively validated as well */
53 __msg("Validating global_good() func")
54 __msg("('global_good') is safe for any args that match its prototype")
55 __msg("insns processed {{[0-9]+\\+[0-9]+\\+[0-9]+$}}")
56 int chained_global_func_calls_success(void)
57 {
58 	int sum = 0;
59 
60 	if (call_dead_subprog)
61 		sum += global_dead();
62 	return global_calls_good_only() + sum;
63 }
64 
65 SEC("?raw_tp")
66 __failure __log_level(2)
67 /* main prog validated successfully first */
68 __msg("('global_calls_bad') is global and assumed valid.")
69 /* eventually we validate global_bad() and fail */
70 __msg("Validating global_bad() func")
71 __msg("math between map_value pointer and register") /* BOOM */
72 int chained_global_func_calls_bad(void)
73 {
74 	return global_calls_bad();
75 }
76 
77 /* do out of bounds access forcing verifier to fail verification if this
78  * global func is called
79  */
80 __noinline int global_unsupp(const int *mem)
81 {
82 	if (!mem)
83 		return 0;
84 	return mem[100]; /* BOOM */
85 }
86 
87 const volatile bool skip_unsupp_global = true;
88 
89 SEC("?raw_tp")
90 __success
91 int guarded_unsupp_global_called(void)
92 {
93 	if (!skip_unsupp_global)
94 		return global_unsupp(NULL);
95 	return 0;
96 }
97 
98 SEC("?raw_tp")
99 __failure __log_level(2)
100 __msg("Func#1 ('global_unsupp') is global and assumed valid.")
101 __msg("Validating global_unsupp() func#1...")
102 __msg("value is outside of the allowed memory range")
103 int unguarded_unsupp_global_called(void)
104 {
105 	int x = 0;
106 
107 	return global_unsupp(&x);
108 }
109 
110 long stack[128];
111 
112 __weak int subprog_nullable_ptr_bad(int *p)
113 {
114 	return (*p) * 2; /* bad, missing null check */
115 }
116 
117 SEC("?raw_tp")
118 __failure __log_level(2)
119 __msg("invalid mem access 'mem_or_null'")
120 int arg_tag_nullable_ptr_fail(void *ctx)
121 {
122 	int x = 42;
123 
124 	return subprog_nullable_ptr_bad(&x);
125 }
126 
127 typedef struct {
128 	int x;
129 } user_struct_t;
130 
131 __noinline __weak int subprog_user_anon_mem(user_struct_t *t)
132 {
133 	return t ? t->x : 0;
134 }
135 
136 SEC("?tracepoint")
137 __failure __log_level(2)
138 __msg("Caller passes invalid args into func#1 ('subprog_user_anon_mem')")
139 int anon_user_mem_invalid(void *ctx)
140 {
141 	/* can't pass PTR_TO_CTX as user memory */
142 	return subprog_user_anon_mem(ctx);
143 }
144 
145 SEC("?tracepoint")
146 __success __log_level(2)
147 __msg("Func#1 ('subprog_user_anon_mem') is safe for any args that match its prototype")
148 int anon_user_mem_valid(void *ctx)
149 {
150 	user_struct_t t = { .x = 42 };
151 
152 	return subprog_user_anon_mem(&t);
153 }
154 
155 __noinline __weak int subprog_user_anon_mem_huge(int (*p)[0x3fffffff])
156 {
157 	return p ? (*p)[1] : 0;
158 }
159 
160 SEC("?tracepoint")
161 __failure __log_level(2)
162 __msg("R1 memory size 4294967292 is too large")
163 int anon_user_mem_huge_size_invalid(void *ctx)
164 {
165 	int (*p)[0x3fffffff];
166 	int tiny = 42;
167 
168 	p = (void *)&tiny;
169 	return subprog_user_anon_mem_huge(p) + tiny;
170 }
171 
172 __noinline __weak int subprog_nonnull_ptr_good(int *p1 __arg_nonnull, int *p2 __arg_nonnull)
173 {
174 	return (*p1) * (*p2); /* good, no need for NULL checks */
175 }
176 
177 int x = 47;
178 
179 SEC("?raw_tp")
180 __success __log_level(2)
181 int arg_tag_nonnull_ptr_good(void *ctx)
182 {
183 	int y = 74;
184 
185 	return subprog_nonnull_ptr_good(&x, &y);
186 }
187 
188 /* this global subprog can be now called from many types of entry progs, each
189  * with different context type
190  */
191 __weak int subprog_ctx_tag(void *ctx __arg_ctx)
192 {
193 	return bpf_get_stack(ctx, stack, sizeof(stack), 0);
194 }
195 
196 __weak int raw_tp_canonical(struct bpf_raw_tracepoint_args *ctx __arg_ctx)
197 {
198 	return 0;
199 }
200 
201 __weak int raw_tp_u64_array(u64 *ctx __arg_ctx)
202 {
203 	return 0;
204 }
205 
206 SEC("?raw_tp")
207 __success __log_level(2)
208 int arg_tag_ctx_raw_tp(void *ctx)
209 {
210 	return subprog_ctx_tag(ctx) + raw_tp_canonical(ctx) + raw_tp_u64_array(ctx);
211 }
212 
213 SEC("?raw_tp.w")
214 __success __log_level(2)
215 int arg_tag_ctx_raw_tp_writable(void *ctx)
216 {
217 	return subprog_ctx_tag(ctx) + raw_tp_canonical(ctx) + raw_tp_u64_array(ctx);
218 }
219 
220 SEC("?tp_btf/sys_enter")
221 __success __log_level(2)
222 int arg_tag_ctx_raw_tp_btf(void *ctx)
223 {
224 	return subprog_ctx_tag(ctx) + raw_tp_canonical(ctx) + raw_tp_u64_array(ctx);
225 }
226 
227 struct whatever { };
228 
229 __weak int tp_whatever(struct whatever *ctx __arg_ctx)
230 {
231 	return 0;
232 }
233 
234 SEC("?tp")
235 __success __log_level(2)
236 int arg_tag_ctx_tp(void *ctx)
237 {
238 	return subprog_ctx_tag(ctx) + tp_whatever(ctx);
239 }
240 
241 __weak int kprobe_subprog_pt_regs(struct pt_regs *ctx __arg_ctx)
242 {
243 	return 0;
244 }
245 
246 __weak int kprobe_subprog_typedef(bpf_user_pt_regs_t *ctx __arg_ctx)
247 {
248 	return 0;
249 }
250 
251 SEC("?kprobe")
252 __success __log_level(2)
253 int arg_tag_ctx_kprobe(void *ctx)
254 {
255 	return subprog_ctx_tag(ctx) +
256 	       kprobe_subprog_pt_regs(ctx) +
257 	       kprobe_subprog_typedef(ctx);
258 }
259 
260 __weak int perf_subprog_regs(
261 #if defined(bpf_target_riscv)
262 	struct user_regs_struct *ctx __arg_ctx
263 #elif defined(bpf_target_s390)
264 	/* user_pt_regs typedef is anonymous struct, so only `void *` works */
265 	void *ctx __arg_ctx
266 #elif defined(bpf_target_loongarch) || defined(bpf_target_arm64) || defined(bpf_target_powerpc)
267 	struct user_pt_regs *ctx __arg_ctx
268 #else
269 	struct pt_regs *ctx __arg_ctx
270 #endif
271 )
272 {
273 	return 0;
274 }
275 
276 __weak int perf_subprog_typedef(bpf_user_pt_regs_t *ctx __arg_ctx)
277 {
278 	return 0;
279 }
280 
281 __weak int perf_subprog_canonical(struct bpf_perf_event_data *ctx __arg_ctx)
282 {
283 	return 0;
284 }
285 
286 SEC("?perf_event")
287 __success __log_level(2)
288 int arg_tag_ctx_perf(void *ctx)
289 {
290 	return subprog_ctx_tag(ctx) +
291 	       perf_subprog_regs(ctx) +
292 	       perf_subprog_typedef(ctx) +
293 	       perf_subprog_canonical(ctx);
294 }
295 
296 __weak int iter_subprog_void(void *ctx __arg_ctx)
297 {
298 	return 0;
299 }
300 
301 __weak int iter_subprog_typed(struct bpf_iter__task *ctx __arg_ctx)
302 {
303 	return 0;
304 }
305 
306 SEC("?iter/task")
307 __success __log_level(2)
308 int arg_tag_ctx_iter_task(struct bpf_iter__task *ctx)
309 {
310 	return (iter_subprog_void(ctx) + iter_subprog_typed(ctx)) & 1;
311 }
312 
313 __weak int tracing_subprog_void(void *ctx __arg_ctx)
314 {
315 	return 0;
316 }
317 
318 __weak int tracing_subprog_u64(u64 *ctx __arg_ctx)
319 {
320 	return 0;
321 }
322 
323 int acc;
324 
325 SEC("?fentry/" SYS_PREFIX "sys_nanosleep")
326 __success __log_level(2)
327 int BPF_PROG(arg_tag_ctx_fentry)
328 {
329 	acc += tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
330 	return 0;
331 }
332 
333 SEC("?fexit/" SYS_PREFIX "sys_nanosleep")
334 __success __log_level(2)
335 int BPF_PROG(arg_tag_ctx_fexit)
336 {
337 	acc += tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
338 	return 0;
339 }
340 
341 SEC("?fmod_ret/" SYS_PREFIX "sys_nanosleep")
342 __success __log_level(2)
343 int BPF_PROG(arg_tag_ctx_fmod_ret)
344 {
345 	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
346 }
347 
348 SEC("?lsm/bpf")
349 __success __log_level(2)
350 int BPF_PROG(arg_tag_ctx_lsm)
351 {
352 	int ret;
353 
354 	ret = tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
355 	set_if_not_errno_or_zero(ret, -1);
356 	return ret;
357 }
358 
359 SEC("?struct_ops/test_1")
360 __success __log_level(2)
361 int BPF_PROG(arg_tag_ctx_struct_ops)
362 {
363 	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
364 }
365 
366 SEC(".struct_ops")
367 struct bpf_dummy_ops dummy_1 = {
368 	.test_1 = (void *)arg_tag_ctx_struct_ops,
369 };
370 
371 SEC("?syscall")
372 __success __log_level(2)
373 int arg_tag_ctx_syscall(void *ctx)
374 {
375 	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx) + tp_whatever(ctx);
376 }
377 
378 __weak int syscall_array_bpf_for(void *ctx __arg_ctx)
379 {
380 	int *arr = ctx;
381 	int i;
382 
383 	bpf_for(i, 0, 100)
384 		arr[i] *= i;
385 
386 	return 0;
387 }
388 
389 SEC("?syscall")
390 __success __log_level(2)
391 int arg_tag_ctx_syscall_bpf_for(void *ctx)
392 {
393 	return syscall_array_bpf_for(ctx);
394 }
395 
396 SEC("syscall")
397 __auxiliary
398 int syscall_tailcall_target(void *ctx)
399 {
400 	return syscall_array_bpf_for(ctx);
401 }
402 
403 struct {
404 	__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
405 	__uint(max_entries, 1);
406 	__uint(key_size, sizeof(__u32));
407 	__array(values, int (void *));
408 } syscall_prog_array SEC(".maps") = {
409 	.values = {
410 		[0] = (void *)&syscall_tailcall_target,
411 	},
412 };
413 
414 SEC("?syscall")
415 __success __log_level(2)
416 int arg_tag_ctx_syscall_tailcall(void *ctx)
417 {
418 	bpf_tail_call(ctx, &syscall_prog_array, 0);
419 	return 0;
420 }
421 
422 SEC("?syscall")
423 __failure __log_level(2)
424 __msg("dereference of modified ctx ptr R1 off=8 disallowed")
425 int arg_tag_ctx_syscall_tailcall_fixed_off_bad(void *ctx)
426 {
427 	char *p = ctx;
428 
429 	p += 8;
430 	bpf_tail_call(p, &syscall_prog_array, 0);
431 	return 0;
432 }
433 
434 SEC("?syscall")
435 __failure __log_level(2)
436 __msg("variable ctx access var_off=(0x0; 0x4) disallowed")
437 int arg_tag_ctx_syscall_tailcall_var_off_bad(void *ctx)
438 {
439 	__u64 off = bpf_get_prandom_u32();
440 	char *p = ctx;
441 
442 	off &= 4;
443 	p += off;
444 	bpf_tail_call(p, &syscall_prog_array, 0);
445 	return 0;
446 }
447 
448 SEC("?syscall")
449 __failure __log_level(2)
450 __msg("dereference of modified ctx ptr R1 off=8 disallowed")
451 int arg_tag_ctx_syscall_fixed_off_bad(void *ctx)
452 {
453 	char *p = ctx;
454 
455 	p += 8;
456 	return subprog_ctx_tag(p);
457 }
458 
459 SEC("?syscall")
460 __failure __log_level(2)
461 __msg("variable ctx access var_off=(0x0; 0x4) disallowed")
462 int arg_tag_ctx_syscall_var_off_bad(void *ctx)
463 {
464 	__u64 off = bpf_get_prandom_u32();
465 	char *p = ctx;
466 
467 	off &= 4;
468 	p += off;
469 	return subprog_ctx_tag(p);
470 }
471 
472 __weak int subprog_dynptr(struct bpf_dynptr *dptr)
473 {
474 	long *d, t, buf[1] = {};
475 
476 	d = bpf_dynptr_data(dptr, 0, sizeof(long));
477 	if (!d)
478 		return 0;
479 
480 	t = *d + 1;
481 
482 	d = bpf_dynptr_slice(dptr, 0, &buf, sizeof(long));
483 	if (!d)
484 		return t;
485 
486 	t = *d + 2;
487 
488 	return t;
489 }
490 
491 SEC("?xdp")
492 __success __log_level(2)
493 int arg_tag_dynptr(struct xdp_md *ctx)
494 {
495 	struct bpf_dynptr dptr;
496 
497 	bpf_dynptr_from_xdp(ctx, 0, &dptr);
498 
499 	return subprog_dynptr(&dptr);
500 }
501 
502 __weak
503 void foo(void)
504 {
505 }
506 
507 SEC("?tc")
508 __failure __msg("R0 !read_ok")
509 int return_from_void_global(struct __sk_buff *skb)
510 {
511 	foo();
512 
513 	asm volatile(
514 		"r1 = r0;"
515 		:::
516 	);
517 
518 	return 0;
519 }
520 
521 char _license[] SEC("license") = "GPL";
522