1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <vmlinux.h> |
3 | #include <bpf/bpf_tracing.h> |
4 | #include <bpf/bpf_helpers.h> |
5 | #include <bpf/bpf_core_read.h> |
6 | #include "bpf_misc.h" |
7 | #include "../bpf_testmod/bpf_testmod_kfunc.h" |
8 | |
9 | struct map_value { |
10 | char buf[8]; |
11 | struct prog_test_ref_kfunc __kptr_untrusted *unref_ptr; |
12 | struct prog_test_ref_kfunc __kptr *ref_ptr; |
13 | struct prog_test_member __kptr *ref_memb_ptr; |
14 | }; |
15 | |
16 | struct array_map { |
17 | __uint(type, BPF_MAP_TYPE_ARRAY); |
18 | __type(key, int); |
19 | __type(value, struct map_value); |
20 | __uint(max_entries, 1); |
21 | } array_map SEC(".maps" ); |
22 | |
23 | SEC("?tc" ) |
24 | __failure __msg("kptr access size must be BPF_DW" ) |
25 | int size_not_bpf_dw(struct __sk_buff *ctx) |
26 | { |
27 | struct map_value *v; |
28 | int key = 0; |
29 | |
30 | v = bpf_map_lookup_elem(&array_map, &key); |
31 | if (!v) |
32 | return 0; |
33 | |
34 | *(u32 *)&v->unref_ptr = 0; |
35 | return 0; |
36 | } |
37 | |
38 | SEC("?tc" ) |
39 | __failure __msg("kptr access cannot have variable offset" ) |
40 | int non_const_var_off(struct __sk_buff *ctx) |
41 | { |
42 | struct map_value *v; |
43 | int key = 0, id; |
44 | |
45 | v = bpf_map_lookup_elem(&array_map, &key); |
46 | if (!v) |
47 | return 0; |
48 | |
49 | id = ctx->protocol; |
50 | if (id < 4 || id > 12) |
51 | return 0; |
52 | *(u64 *)((void *)v + id) = 0; |
53 | |
54 | return 0; |
55 | } |
56 | |
57 | SEC("?tc" ) |
58 | __failure __msg("R1 doesn't have constant offset. kptr has to be" ) |
59 | int non_const_var_off_kptr_xchg(struct __sk_buff *ctx) |
60 | { |
61 | struct map_value *v; |
62 | int key = 0, id; |
63 | |
64 | v = bpf_map_lookup_elem(&array_map, &key); |
65 | if (!v) |
66 | return 0; |
67 | |
68 | id = ctx->protocol; |
69 | if (id < 4 || id > 12) |
70 | return 0; |
71 | bpf_kptr_xchg((void *)v + id, NULL); |
72 | |
73 | return 0; |
74 | } |
75 | |
76 | SEC("?tc" ) |
77 | __failure __msg("kptr access misaligned expected=8 off=7" ) |
78 | int misaligned_access_write(struct __sk_buff *ctx) |
79 | { |
80 | struct map_value *v; |
81 | int key = 0; |
82 | |
83 | v = bpf_map_lookup_elem(&array_map, &key); |
84 | if (!v) |
85 | return 0; |
86 | |
87 | *(void **)((void *)v + 7) = NULL; |
88 | |
89 | return 0; |
90 | } |
91 | |
92 | SEC("?tc" ) |
93 | __failure __msg("kptr access misaligned expected=8 off=1" ) |
94 | int misaligned_access_read(struct __sk_buff *ctx) |
95 | { |
96 | struct map_value *v; |
97 | int key = 0; |
98 | |
99 | v = bpf_map_lookup_elem(&array_map, &key); |
100 | if (!v) |
101 | return 0; |
102 | |
103 | return *(u64 *)((void *)v + 1); |
104 | } |
105 | |
106 | SEC("?tc" ) |
107 | __failure __msg("variable untrusted_ptr_ access var_off=(0x0; 0x1e0)" ) |
108 | int reject_var_off_store(struct __sk_buff *ctx) |
109 | { |
110 | struct prog_test_ref_kfunc *unref_ptr; |
111 | struct map_value *v; |
112 | int key = 0, id; |
113 | |
114 | v = bpf_map_lookup_elem(&array_map, &key); |
115 | if (!v) |
116 | return 0; |
117 | |
118 | unref_ptr = v->unref_ptr; |
119 | if (!unref_ptr) |
120 | return 0; |
121 | id = ctx->protocol; |
122 | if (id < 4 || id > 12) |
123 | return 0; |
124 | unref_ptr += id; |
125 | v->unref_ptr = unref_ptr; |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | SEC("?tc" ) |
131 | __failure __msg("invalid kptr access, R1 type=untrusted_ptr_prog_test_ref_kfunc" ) |
132 | int reject_bad_type_match(struct __sk_buff *ctx) |
133 | { |
134 | struct prog_test_ref_kfunc *unref_ptr; |
135 | struct map_value *v; |
136 | int key = 0; |
137 | |
138 | v = bpf_map_lookup_elem(&array_map, &key); |
139 | if (!v) |
140 | return 0; |
141 | |
142 | unref_ptr = v->unref_ptr; |
143 | if (!unref_ptr) |
144 | return 0; |
145 | unref_ptr = (void *)unref_ptr + 4; |
146 | v->unref_ptr = unref_ptr; |
147 | |
148 | return 0; |
149 | } |
150 | |
151 | SEC("?tc" ) |
152 | __failure __msg("R1 type=untrusted_ptr_or_null_ expected=percpu_ptr_" ) |
153 | int marked_as_untrusted_or_null(struct __sk_buff *ctx) |
154 | { |
155 | struct map_value *v; |
156 | int key = 0; |
157 | |
158 | v = bpf_map_lookup_elem(&array_map, &key); |
159 | if (!v) |
160 | return 0; |
161 | |
162 | bpf_this_cpu_ptr(v->unref_ptr); |
163 | return 0; |
164 | } |
165 | |
166 | SEC("?tc" ) |
167 | __failure __msg("access beyond struct prog_test_ref_kfunc at off 32 size 4" ) |
168 | int correct_btf_id_check_size(struct __sk_buff *ctx) |
169 | { |
170 | struct prog_test_ref_kfunc *p; |
171 | struct map_value *v; |
172 | int key = 0; |
173 | |
174 | v = bpf_map_lookup_elem(&array_map, &key); |
175 | if (!v) |
176 | return 0; |
177 | |
178 | p = v->unref_ptr; |
179 | if (!p) |
180 | return 0; |
181 | return *(int *)((void *)p + bpf_core_type_size(struct prog_test_ref_kfunc)); |
182 | } |
183 | |
184 | SEC("?tc" ) |
185 | __failure __msg("R1 type=untrusted_ptr_ expected=percpu_ptr_" ) |
186 | int inherit_untrusted_on_walk(struct __sk_buff *ctx) |
187 | { |
188 | struct prog_test_ref_kfunc *unref_ptr; |
189 | struct map_value *v; |
190 | int key = 0; |
191 | |
192 | v = bpf_map_lookup_elem(&array_map, &key); |
193 | if (!v) |
194 | return 0; |
195 | |
196 | unref_ptr = v->unref_ptr; |
197 | if (!unref_ptr) |
198 | return 0; |
199 | unref_ptr = unref_ptr->next; |
200 | bpf_this_cpu_ptr(unref_ptr); |
201 | return 0; |
202 | } |
203 | |
204 | SEC("?tc" ) |
205 | __failure __msg("off=8 kptr isn't referenced kptr" ) |
206 | int reject_kptr_xchg_on_unref(struct __sk_buff *ctx) |
207 | { |
208 | struct map_value *v; |
209 | int key = 0; |
210 | |
211 | v = bpf_map_lookup_elem(&array_map, &key); |
212 | if (!v) |
213 | return 0; |
214 | |
215 | bpf_kptr_xchg(&v->unref_ptr, NULL); |
216 | return 0; |
217 | } |
218 | |
219 | SEC("?tc" ) |
220 | __failure __msg("R1 type=rcu_ptr_or_null_ expected=percpu_ptr_" ) |
221 | int mark_ref_as_untrusted_or_null(struct __sk_buff *ctx) |
222 | { |
223 | struct map_value *v; |
224 | int key = 0; |
225 | |
226 | v = bpf_map_lookup_elem(&array_map, &key); |
227 | if (!v) |
228 | return 0; |
229 | |
230 | bpf_this_cpu_ptr(v->ref_ptr); |
231 | return 0; |
232 | } |
233 | |
234 | SEC("?tc" ) |
235 | __failure __msg("store to referenced kptr disallowed" ) |
236 | int reject_untrusted_store_to_ref(struct __sk_buff *ctx) |
237 | { |
238 | struct prog_test_ref_kfunc *p; |
239 | struct map_value *v; |
240 | int key = 0; |
241 | |
242 | v = bpf_map_lookup_elem(&array_map, &key); |
243 | if (!v) |
244 | return 0; |
245 | |
246 | p = v->ref_ptr; |
247 | if (!p) |
248 | return 0; |
249 | /* Checkmate, clang */ |
250 | *(struct prog_test_ref_kfunc * volatile *)&v->ref_ptr = p; |
251 | return 0; |
252 | } |
253 | |
254 | SEC("?tc" ) |
255 | __failure __msg("R2 must be referenced" ) |
256 | int reject_untrusted_xchg(struct __sk_buff *ctx) |
257 | { |
258 | struct prog_test_ref_kfunc *p; |
259 | struct map_value *v; |
260 | int key = 0; |
261 | |
262 | v = bpf_map_lookup_elem(&array_map, &key); |
263 | if (!v) |
264 | return 0; |
265 | |
266 | p = v->ref_ptr; |
267 | if (!p) |
268 | return 0; |
269 | bpf_kptr_xchg(&v->ref_ptr, p); |
270 | return 0; |
271 | } |
272 | |
273 | SEC("?tc" ) |
274 | __failure |
275 | __msg("invalid kptr access, R2 type=ptr_prog_test_ref_kfunc expected=ptr_prog_test_member" ) |
276 | int reject_bad_type_xchg(struct __sk_buff *ctx) |
277 | { |
278 | struct prog_test_ref_kfunc *ref_ptr; |
279 | struct map_value *v; |
280 | int key = 0; |
281 | |
282 | v = bpf_map_lookup_elem(&array_map, &key); |
283 | if (!v) |
284 | return 0; |
285 | |
286 | ref_ptr = bpf_kfunc_call_test_acquire(scalar_ptr: &(unsigned long){0}); |
287 | if (!ref_ptr) |
288 | return 0; |
289 | bpf_kptr_xchg(&v->ref_memb_ptr, ref_ptr); |
290 | return 0; |
291 | } |
292 | |
293 | SEC("?tc" ) |
294 | __failure __msg("invalid kptr access, R2 type=ptr_prog_test_ref_kfunc" ) |
295 | int reject_member_of_ref_xchg(struct __sk_buff *ctx) |
296 | { |
297 | struct prog_test_ref_kfunc *ref_ptr; |
298 | struct map_value *v; |
299 | int key = 0; |
300 | |
301 | v = bpf_map_lookup_elem(&array_map, &key); |
302 | if (!v) |
303 | return 0; |
304 | |
305 | ref_ptr = bpf_kfunc_call_test_acquire(scalar_ptr: &(unsigned long){0}); |
306 | if (!ref_ptr) |
307 | return 0; |
308 | bpf_kptr_xchg(&v->ref_memb_ptr, &ref_ptr->memb); |
309 | return 0; |
310 | } |
311 | |
312 | SEC("?syscall" ) |
313 | __failure __msg("kptr cannot be accessed indirectly by helper" ) |
314 | int reject_indirect_helper_access(struct __sk_buff *ctx) |
315 | { |
316 | struct map_value *v; |
317 | int key = 0; |
318 | |
319 | v = bpf_map_lookup_elem(&array_map, &key); |
320 | if (!v) |
321 | return 0; |
322 | |
323 | bpf_get_current_comm(v, sizeof(v->buf) + 1); |
324 | return 0; |
325 | } |
326 | |
327 | __noinline |
328 | int write_func(int *p) |
329 | { |
330 | return p ? *p = 42 : 0; |
331 | } |
332 | |
333 | SEC("?tc" ) |
334 | __failure __msg("kptr cannot be accessed indirectly by helper" ) |
335 | int reject_indirect_global_func_access(struct __sk_buff *ctx) |
336 | { |
337 | struct map_value *v; |
338 | int key = 0; |
339 | |
340 | v = bpf_map_lookup_elem(&array_map, &key); |
341 | if (!v) |
342 | return 0; |
343 | |
344 | return write_func((void *)v + 5); |
345 | } |
346 | |
347 | SEC("?tc" ) |
348 | __failure __msg("Unreleased reference id=5 alloc_insn=" ) |
349 | int kptr_xchg_ref_state(struct __sk_buff *ctx) |
350 | { |
351 | struct prog_test_ref_kfunc *p; |
352 | struct map_value *v; |
353 | int key = 0; |
354 | |
355 | v = bpf_map_lookup_elem(&array_map, &key); |
356 | if (!v) |
357 | return 0; |
358 | |
359 | p = bpf_kfunc_call_test_acquire(scalar_ptr: &(unsigned long){0}); |
360 | if (!p) |
361 | return 0; |
362 | bpf_kptr_xchg(&v->ref_ptr, p); |
363 | return 0; |
364 | } |
365 | |
366 | SEC("?tc" ) |
367 | __failure __msg("Possibly NULL pointer passed to helper arg2" ) |
368 | int kptr_xchg_possibly_null(struct __sk_buff *ctx) |
369 | { |
370 | struct prog_test_ref_kfunc *p; |
371 | struct map_value *v; |
372 | int key = 0; |
373 | |
374 | v = bpf_map_lookup_elem(&array_map, &key); |
375 | if (!v) |
376 | return 0; |
377 | |
378 | p = bpf_kfunc_call_test_acquire(scalar_ptr: &(unsigned long){0}); |
379 | |
380 | /* PTR_TO_BTF_ID | PTR_MAYBE_NULL passed to bpf_kptr_xchg() */ |
381 | p = bpf_kptr_xchg(&v->ref_ptr, p); |
382 | if (p) |
383 | bpf_kfunc_call_test_release(p); |
384 | |
385 | return 0; |
386 | } |
387 | |
388 | char _license[] SEC("license" ) = "GPL" ; |
389 | |