xref: /linux/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c (revision 1b1934dbbdcf9aa2d507932ff488cec47999cf3f)
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_misc.h"
7 #include "xdp_metadata.h"
8 #include "bpf_kfuncs.h"
9 
10 int arr[1];
11 int unkn_idx;
12 const volatile bool call_dead_subprog = false;
13 
14 __noinline long global_bad(void)
15 {
16 	return arr[unkn_idx]; /* BOOM */
17 }
18 
19 __noinline long global_good(void)
20 {
21 	return arr[0];
22 }
23 
24 __noinline long global_calls_bad(void)
25 {
26 	return global_good() + global_bad() /* does BOOM indirectly */;
27 }
28 
29 __noinline long global_calls_good_only(void)
30 {
31 	return global_good();
32 }
33 
34 __noinline long global_dead(void)
35 {
36 	return arr[0] * 2;
37 }
38 
39 SEC("?raw_tp")
40 __success __log_level(2)
41 /* main prog is validated completely first */
42 __msg("('global_calls_good_only') is global and assumed valid.")
43 /* eventually global_good() is transitively validated as well */
44 __msg("Validating global_good() func")
45 __msg("('global_good') is safe for any args that match its prototype")
46 int chained_global_func_calls_success(void)
47 {
48 	int sum = 0;
49 
50 	if (call_dead_subprog)
51 		sum += global_dead();
52 	return global_calls_good_only() + sum;
53 }
54 
55 SEC("?raw_tp")
56 __failure __log_level(2)
57 /* main prog validated successfully first */
58 __msg("('global_calls_bad') is global and assumed valid.")
59 /* eventually we validate global_bad() and fail */
60 __msg("Validating global_bad() func")
61 __msg("math between map_value pointer and register") /* BOOM */
62 int chained_global_func_calls_bad(void)
63 {
64 	return global_calls_bad();
65 }
66 
67 /* do out of bounds access forcing verifier to fail verification if this
68  * global func is called
69  */
70 __noinline int global_unsupp(const int *mem)
71 {
72 	if (!mem)
73 		return 0;
74 	return mem[100]; /* BOOM */
75 }
76 
77 const volatile bool skip_unsupp_global = true;
78 
79 SEC("?raw_tp")
80 __success
81 int guarded_unsupp_global_called(void)
82 {
83 	if (!skip_unsupp_global)
84 		return global_unsupp(NULL);
85 	return 0;
86 }
87 
88 SEC("?raw_tp")
89 __failure __log_level(2)
90 __msg("Func#1 ('global_unsupp') is global and assumed valid.")
91 __msg("Validating global_unsupp() func#1...")
92 __msg("value is outside of the allowed memory range")
93 int unguarded_unsupp_global_called(void)
94 {
95 	int x = 0;
96 
97 	return global_unsupp(&x);
98 }
99 
100 long stack[128];
101 
102 __weak int subprog_nullable_ptr_bad(int *p)
103 {
104 	return (*p) * 2; /* bad, missing null check */
105 }
106 
107 SEC("?raw_tp")
108 __failure __log_level(2)
109 __msg("invalid mem access 'mem_or_null'")
110 int arg_tag_nullable_ptr_fail(void *ctx)
111 {
112 	int x = 42;
113 
114 	return subprog_nullable_ptr_bad(&x);
115 }
116 
117 __noinline __weak int subprog_nonnull_ptr_good(int *p1 __arg_nonnull, int *p2 __arg_nonnull)
118 {
119 	return (*p1) * (*p2); /* good, no need for NULL checks */
120 }
121 
122 int x = 47;
123 
124 SEC("?raw_tp")
125 __success __log_level(2)
126 int arg_tag_nonnull_ptr_good(void *ctx)
127 {
128 	int y = 74;
129 
130 	return subprog_nonnull_ptr_good(&x, &y);
131 }
132 
133 /* this global subprog can be now called from many types of entry progs, each
134  * with different context type
135  */
136 __weak int subprog_ctx_tag(void *ctx __arg_ctx)
137 {
138 	return bpf_get_stack(ctx, stack, sizeof(stack), 0);
139 }
140 
141 SEC("?raw_tp")
142 __success __log_level(2)
143 int arg_tag_ctx_raw_tp(void *ctx)
144 {
145 	return subprog_ctx_tag(ctx);
146 }
147 
148 SEC("?tp")
149 __success __log_level(2)
150 int arg_tag_ctx_tp(void *ctx)
151 {
152 	return subprog_ctx_tag(ctx);
153 }
154 
155 SEC("?kprobe")
156 __success __log_level(2)
157 int arg_tag_ctx_kprobe(void *ctx)
158 {
159 	return subprog_ctx_tag(ctx);
160 }
161 
162 __weak int subprog_dynptr(struct bpf_dynptr *dptr)
163 {
164 	long *d, t, buf[1] = {};
165 
166 	d = bpf_dynptr_data(dptr, 0, sizeof(long));
167 	if (!d)
168 		return 0;
169 
170 	t = *d + 1;
171 
172 	d = bpf_dynptr_slice(dptr, 0, &buf, sizeof(long));
173 	if (!d)
174 		return t;
175 
176 	t = *d + 2;
177 
178 	return t;
179 }
180 
181 SEC("?xdp")
182 __success __log_level(2)
183 int arg_tag_dynptr(struct xdp_md *ctx)
184 {
185 	struct bpf_dynptr dptr;
186 
187 	bpf_dynptr_from_xdp(ctx, 0, &dptr);
188 
189 	return subprog_dynptr(&dptr);
190 }
191 
192 char _license[] SEC("license") = "GPL";
193