1 // SPDX-License-Identifier: GPL-2.0
2 /* Converted from tools/testing/selftests/bpf/verifier/array_access.c */
3
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "bpf_misc.h"
7
8 #define MAX_ENTRIES 11
9
10 struct test_val {
11 unsigned int index;
12 int foo[MAX_ENTRIES];
13 };
14
15 struct {
16 __uint(type, BPF_MAP_TYPE_ARRAY);
17 __uint(max_entries, 1);
18 __type(key, int);
19 __type(value, struct test_val);
20 __uint(map_flags, BPF_F_RDONLY_PROG);
21 } map_array_ro SEC(".maps");
22
23 struct {
24 __uint(type, BPF_MAP_TYPE_ARRAY);
25 __uint(max_entries, 1);
26 __type(key, int);
27 __type(value, struct test_val);
28 __uint(map_flags, BPF_F_WRONLY_PROG);
29 } map_array_wo SEC(".maps");
30
31 struct {
32 __uint(type, BPF_MAP_TYPE_HASH);
33 __uint(max_entries, 1);
34 __type(key, long long);
35 __type(value, struct test_val);
36 } map_hash_48b SEC(".maps");
37
38 SEC("socket")
39 __description("valid map access into an array with a constant")
40 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
41 __retval(0)
an_array_with_a_constant_1(void)42 __naked void an_array_with_a_constant_1(void)
43 {
44 asm volatile (" \
45 r1 = 0; \
46 *(u64*)(r10 - 8) = r1; \
47 r2 = r10; \
48 r2 += -8; \
49 r1 = %[map_hash_48b] ll; \
50 call %[bpf_map_lookup_elem]; \
51 if r0 == 0 goto l0_%=; \
52 r1 = %[test_val_foo]; \
53 *(u64*)(r0 + 0) = r1; \
54 l0_%=: exit; \
55 " :
56 : __imm(bpf_map_lookup_elem),
57 __imm_addr(map_hash_48b),
58 __imm_const(test_val_foo, offsetof(struct test_val, foo))
59 : __clobber_all);
60 }
61
62 SEC("socket")
63 __description("valid map access into an array with a register")
64 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
__flag(BPF_F_ANY_ALIGNMENT)65 __retval(0) __flag(BPF_F_ANY_ALIGNMENT)
66 __naked void an_array_with_a_register_1(void)
67 {
68 asm volatile (" \
69 r1 = 0; \
70 *(u64*)(r10 - 8) = r1; \
71 r2 = r10; \
72 r2 += -8; \
73 r1 = %[map_hash_48b] ll; \
74 call %[bpf_map_lookup_elem]; \
75 if r0 == 0 goto l0_%=; \
76 r1 = 4; \
77 r1 <<= 2; \
78 r0 += r1; \
79 r1 = %[test_val_foo]; \
80 *(u64*)(r0 + 0) = r1; \
81 l0_%=: exit; \
82 " :
83 : __imm(bpf_map_lookup_elem),
84 __imm_addr(map_hash_48b),
85 __imm_const(test_val_foo, offsetof(struct test_val, foo))
86 : __clobber_all);
87 }
88
89 SEC("socket")
90 __description("valid map access into an array with a variable")
91 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
__flag(BPF_F_ANY_ALIGNMENT)92 __retval(0) __flag(BPF_F_ANY_ALIGNMENT)
93 __naked void an_array_with_a_variable_1(void)
94 {
95 asm volatile (" \
96 r1 = 0; \
97 *(u64*)(r10 - 8) = r1; \
98 r2 = r10; \
99 r2 += -8; \
100 r1 = %[map_hash_48b] ll; \
101 call %[bpf_map_lookup_elem]; \
102 if r0 == 0 goto l0_%=; \
103 r1 = *(u32*)(r0 + 0); \
104 if r1 >= %[max_entries] goto l0_%=; \
105 r1 <<= 2; \
106 r0 += r1; \
107 r1 = %[test_val_foo]; \
108 *(u64*)(r0 + 0) = r1; \
109 l0_%=: exit; \
110 " :
111 : __imm(bpf_map_lookup_elem),
112 __imm_addr(map_hash_48b),
113 __imm_const(max_entries, MAX_ENTRIES),
114 __imm_const(test_val_foo, offsetof(struct test_val, foo))
115 : __clobber_all);
116 }
117
118 SEC("socket")
119 __description("valid map access into an array with a signed variable")
120 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
__flag(BPF_F_ANY_ALIGNMENT)121 __retval(0) __flag(BPF_F_ANY_ALIGNMENT)
122 __naked void array_with_a_signed_variable(void)
123 {
124 asm volatile (" \
125 r1 = 0; \
126 *(u64*)(r10 - 8) = r1; \
127 r2 = r10; \
128 r2 += -8; \
129 r1 = %[map_hash_48b] ll; \
130 call %[bpf_map_lookup_elem]; \
131 if r0 == 0 goto l0_%=; \
132 r1 = *(u32*)(r0 + 0); \
133 if w1 s> 0xffffffff goto l1_%=; \
134 w1 = 0; \
135 l1_%=: w2 = %[max_entries]; \
136 if r2 s> r1 goto l2_%=; \
137 w1 = 0; \
138 l2_%=: w1 <<= 2; \
139 r0 += r1; \
140 r1 = %[test_val_foo]; \
141 *(u64*)(r0 + 0) = r1; \
142 l0_%=: exit; \
143 " :
144 : __imm(bpf_map_lookup_elem),
145 __imm_addr(map_hash_48b),
146 __imm_const(max_entries, MAX_ENTRIES),
147 __imm_const(test_val_foo, offsetof(struct test_val, foo))
148 : __clobber_all);
149 }
150
151 SEC("socket")
152 __description("invalid map access into an array with a constant")
153 __failure __msg("invalid access to map value, value_size=48 off=48 size=8")
154 __failure_unpriv
an_array_with_a_constant_2(void)155 __naked void an_array_with_a_constant_2(void)
156 {
157 asm volatile (" \
158 r1 = 0; \
159 *(u64*)(r10 - 8) = r1; \
160 r2 = r10; \
161 r2 += -8; \
162 r1 = %[map_hash_48b] ll; \
163 call %[bpf_map_lookup_elem]; \
164 if r0 == 0 goto l0_%=; \
165 r1 = %[test_val_foo]; \
166 *(u64*)(r0 + %[__imm_0]) = r1; \
167 l0_%=: exit; \
168 " :
169 : __imm(bpf_map_lookup_elem),
170 __imm_addr(map_hash_48b),
171 __imm_const(__imm_0, (MAX_ENTRIES + 1) << 2),
172 __imm_const(test_val_foo, offsetof(struct test_val, foo))
173 : __clobber_all);
174 }
175
176 SEC("socket")
177 __description("invalid map access into an array with a register")
178 __failure __msg("R0 min value is outside of the allowed memory range")
179 __failure_unpriv
__flag(BPF_F_ANY_ALIGNMENT)180 __flag(BPF_F_ANY_ALIGNMENT)
181 __naked void an_array_with_a_register_2(void)
182 {
183 asm volatile (" \
184 r1 = 0; \
185 *(u64*)(r10 - 8) = r1; \
186 r2 = r10; \
187 r2 += -8; \
188 r1 = %[map_hash_48b] ll; \
189 call %[bpf_map_lookup_elem]; \
190 if r0 == 0 goto l0_%=; \
191 r1 = %[__imm_0]; \
192 r1 <<= 2; \
193 r0 += r1; \
194 r1 = %[test_val_foo]; \
195 *(u64*)(r0 + 0) = r1; \
196 l0_%=: exit; \
197 " :
198 : __imm(bpf_map_lookup_elem),
199 __imm_addr(map_hash_48b),
200 __imm_const(__imm_0, MAX_ENTRIES + 1),
201 __imm_const(test_val_foo, offsetof(struct test_val, foo))
202 : __clobber_all);
203 }
204
205 SEC("socket")
206 __description("invalid map access into an array with a variable")
207 __failure
208 __msg("R0 unbounded memory access, make sure to bounds check any such access")
209 __failure_unpriv
__flag(BPF_F_ANY_ALIGNMENT)210 __flag(BPF_F_ANY_ALIGNMENT)
211 __naked void an_array_with_a_variable_2(void)
212 {
213 asm volatile (" \
214 r1 = 0; \
215 *(u64*)(r10 - 8) = r1; \
216 r2 = r10; \
217 r2 += -8; \
218 r1 = %[map_hash_48b] ll; \
219 call %[bpf_map_lookup_elem]; \
220 if r0 == 0 goto l0_%=; \
221 r1 = *(u32*)(r0 + 0); \
222 r1 <<= 2; \
223 r0 += r1; \
224 r1 = %[test_val_foo]; \
225 *(u64*)(r0 + 0) = r1; \
226 l0_%=: exit; \
227 " :
228 : __imm(bpf_map_lookup_elem),
229 __imm_addr(map_hash_48b),
230 __imm_const(test_val_foo, offsetof(struct test_val, foo))
231 : __clobber_all);
232 }
233
234 SEC("socket")
235 __description("invalid map access into an array with no floor check")
236 __failure __msg("R0 unbounded memory access")
237 __failure_unpriv __msg_unpriv("R0 leaks addr")
__flag(BPF_F_ANY_ALIGNMENT)238 __flag(BPF_F_ANY_ALIGNMENT)
239 __naked void array_with_no_floor_check(void)
240 {
241 asm volatile (" \
242 r1 = 0; \
243 *(u64*)(r10 - 8) = r1; \
244 r2 = r10; \
245 r2 += -8; \
246 r1 = %[map_hash_48b] ll; \
247 call %[bpf_map_lookup_elem]; \
248 if r0 == 0 goto l0_%=; \
249 r1 = *(u64*)(r0 + 0); \
250 w2 = %[max_entries]; \
251 if r2 s> r1 goto l1_%=; \
252 w1 = 0; \
253 l1_%=: w1 <<= 2; \
254 r0 += r1; \
255 r1 = %[test_val_foo]; \
256 *(u64*)(r0 + 0) = r1; \
257 l0_%=: exit; \
258 " :
259 : __imm(bpf_map_lookup_elem),
260 __imm_addr(map_hash_48b),
261 __imm_const(max_entries, MAX_ENTRIES),
262 __imm_const(test_val_foo, offsetof(struct test_val, foo))
263 : __clobber_all);
264 }
265
266 SEC("socket")
267 __description("invalid map access into an array with a invalid max check")
268 __failure __msg("invalid access to map value, value_size=48 off=44 size=8")
269 __failure_unpriv __msg_unpriv("R0 leaks addr")
__flag(BPF_F_ANY_ALIGNMENT)270 __flag(BPF_F_ANY_ALIGNMENT)
271 __naked void with_a_invalid_max_check_1(void)
272 {
273 asm volatile (" \
274 r1 = 0; \
275 *(u64*)(r10 - 8) = r1; \
276 r2 = r10; \
277 r2 += -8; \
278 r1 = %[map_hash_48b] ll; \
279 call %[bpf_map_lookup_elem]; \
280 if r0 == 0 goto l0_%=; \
281 r1 = *(u32*)(r0 + 0); \
282 w2 = %[__imm_0]; \
283 if r2 > r1 goto l1_%=; \
284 w1 = 0; \
285 l1_%=: w1 <<= 2; \
286 r0 += r1; \
287 r1 = %[test_val_foo]; \
288 *(u64*)(r0 + 0) = r1; \
289 l0_%=: exit; \
290 " :
291 : __imm(bpf_map_lookup_elem),
292 __imm_addr(map_hash_48b),
293 __imm_const(__imm_0, MAX_ENTRIES + 1),
294 __imm_const(test_val_foo, offsetof(struct test_val, foo))
295 : __clobber_all);
296 }
297
298 SEC("socket")
299 __description("invalid map access into an array with a invalid max check")
300 __failure __msg("R0 pointer += pointer")
301 __failure_unpriv
__flag(BPF_F_ANY_ALIGNMENT)302 __flag(BPF_F_ANY_ALIGNMENT)
303 __naked void with_a_invalid_max_check_2(void)
304 {
305 asm volatile (" \
306 r1 = 0; \
307 *(u64*)(r10 - 8) = r1; \
308 r2 = r10; \
309 r2 += -8; \
310 r1 = %[map_hash_48b] ll; \
311 call %[bpf_map_lookup_elem]; \
312 if r0 == 0 goto l0_%=; \
313 r8 = r0; \
314 r1 = 0; \
315 *(u64*)(r10 - 8) = r1; \
316 r2 = r10; \
317 r2 += -8; \
318 r1 = %[map_hash_48b] ll; \
319 call %[bpf_map_lookup_elem]; \
320 if r0 == 0 goto l0_%=; \
321 r0 += r8; \
322 r0 = *(u32*)(r0 + %[test_val_foo]); \
323 l0_%=: exit; \
324 " :
325 : __imm(bpf_map_lookup_elem),
326 __imm_addr(map_hash_48b),
327 __imm_const(test_val_foo, offsetof(struct test_val, foo))
328 : __clobber_all);
329 }
330
331 SEC("socket")
332 __description("valid read map access into a read-only array 1")
333 __success __success_unpriv __retval(28)
a_read_only_array_1_1(void)334 __naked void a_read_only_array_1_1(void)
335 {
336 asm volatile (" \
337 r1 = 0; \
338 *(u64*)(r10 - 8) = r1; \
339 r2 = r10; \
340 r2 += -8; \
341 r1 = %[map_array_ro] ll; \
342 call %[bpf_map_lookup_elem]; \
343 if r0 == 0 goto l0_%=; \
344 r0 = *(u32*)(r0 + 0); \
345 l0_%=: exit; \
346 " :
347 : __imm(bpf_map_lookup_elem),
348 __imm_addr(map_array_ro)
349 : __clobber_all);
350 }
351
352 SEC("tc")
353 __description("valid read map access into a read-only array 2")
354 __success __retval(65507)
a_read_only_array_2_1(void)355 __naked void a_read_only_array_2_1(void)
356 {
357 asm volatile (" \
358 r1 = 0; \
359 *(u64*)(r10 - 8) = r1; \
360 r2 = r10; \
361 r2 += -8; \
362 r1 = %[map_array_ro] ll; \
363 call %[bpf_map_lookup_elem]; \
364 if r0 == 0 goto l0_%=; \
365 r1 = r0; \
366 r2 = 4; \
367 r3 = 0; \
368 r4 = 0; \
369 r5 = 0; \
370 call %[bpf_csum_diff]; \
371 l0_%=: r0 &= 0xffff; \
372 exit; \
373 " :
374 : __imm(bpf_csum_diff),
375 __imm(bpf_map_lookup_elem),
376 __imm_addr(map_array_ro)
377 : __clobber_all);
378 }
379
380 SEC("socket")
381 __description("invalid write map access into a read-only array 1")
382 __failure __msg("write into map forbidden")
383 __failure_unpriv
a_read_only_array_1_2(void)384 __naked void a_read_only_array_1_2(void)
385 {
386 asm volatile (" \
387 r1 = 0; \
388 *(u64*)(r10 - 8) = r1; \
389 r2 = r10; \
390 r2 += -8; \
391 r1 = %[map_array_ro] ll; \
392 call %[bpf_map_lookup_elem]; \
393 if r0 == 0 goto l0_%=; \
394 r1 = 42; \
395 *(u64*)(r0 + 0) = r1; \
396 l0_%=: exit; \
397 " :
398 : __imm(bpf_map_lookup_elem),
399 __imm_addr(map_array_ro)
400 : __clobber_all);
401 }
402
403 SEC("tc")
404 __description("invalid write map access into a read-only array 2")
405 __failure __msg("write into map forbidden")
a_read_only_array_2_2(void)406 __naked void a_read_only_array_2_2(void)
407 {
408 asm volatile (" \
409 r6 = r1; \
410 r1 = 0; \
411 *(u64*)(r10 - 8) = r1; \
412 r2 = r10; \
413 r2 += -8; \
414 r1 = %[map_array_ro] ll; \
415 call %[bpf_map_lookup_elem]; \
416 if r0 == 0 goto l0_%=; \
417 r1 = r6; \
418 r2 = 0; \
419 r3 = r0; \
420 r4 = 8; \
421 call %[bpf_skb_load_bytes]; \
422 l0_%=: exit; \
423 " :
424 : __imm(bpf_map_lookup_elem),
425 __imm(bpf_skb_load_bytes),
426 __imm_addr(map_array_ro)
427 : __clobber_all);
428 }
429
430 SEC("socket")
431 __description("valid write map access into a write-only array 1")
432 __success __success_unpriv __retval(1)
a_write_only_array_1_1(void)433 __naked void a_write_only_array_1_1(void)
434 {
435 asm volatile (" \
436 r1 = 0; \
437 *(u64*)(r10 - 8) = r1; \
438 r2 = r10; \
439 r2 += -8; \
440 r1 = %[map_array_wo] ll; \
441 call %[bpf_map_lookup_elem]; \
442 if r0 == 0 goto l0_%=; \
443 r1 = 42; \
444 *(u64*)(r0 + 0) = r1; \
445 l0_%=: r0 = 1; \
446 exit; \
447 " :
448 : __imm(bpf_map_lookup_elem),
449 __imm_addr(map_array_wo)
450 : __clobber_all);
451 }
452
453 SEC("tc")
454 __description("valid write map access into a write-only array 2")
455 __success __retval(0)
a_write_only_array_2_1(void)456 __naked void a_write_only_array_2_1(void)
457 {
458 asm volatile (" \
459 r6 = r1; \
460 r1 = 0; \
461 *(u64*)(r10 - 8) = r1; \
462 r2 = r10; \
463 r2 += -8; \
464 r1 = %[map_array_wo] ll; \
465 call %[bpf_map_lookup_elem]; \
466 if r0 == 0 goto l0_%=; \
467 r1 = r6; \
468 r2 = 0; \
469 r3 = r0; \
470 r4 = 8; \
471 call %[bpf_skb_load_bytes]; \
472 l0_%=: exit; \
473 " :
474 : __imm(bpf_map_lookup_elem),
475 __imm(bpf_skb_load_bytes),
476 __imm_addr(map_array_wo)
477 : __clobber_all);
478 }
479
480 SEC("socket")
481 __description("invalid read map access into a write-only array 1")
482 __failure __msg("read from map forbidden")
483 __failure_unpriv
a_write_only_array_1_2(void)484 __naked void a_write_only_array_1_2(void)
485 {
486 asm volatile (" \
487 r1 = 0; \
488 *(u64*)(r10 - 8) = r1; \
489 r2 = r10; \
490 r2 += -8; \
491 r1 = %[map_array_wo] ll; \
492 call %[bpf_map_lookup_elem]; \
493 if r0 == 0 goto l0_%=; \
494 r0 = *(u64*)(r0 + 0); \
495 l0_%=: exit; \
496 " :
497 : __imm(bpf_map_lookup_elem),
498 __imm_addr(map_array_wo)
499 : __clobber_all);
500 }
501
502 SEC("tc")
503 __description("invalid read map access into a write-only array 2")
504 __failure __msg("read from map forbidden")
a_write_only_array_2_2(void)505 __naked void a_write_only_array_2_2(void)
506 {
507 asm volatile (" \
508 r1 = 0; \
509 *(u64*)(r10 - 8) = r1; \
510 r2 = r10; \
511 r2 += -8; \
512 r1 = %[map_array_wo] ll; \
513 call %[bpf_map_lookup_elem]; \
514 if r0 == 0 goto l0_%=; \
515 r1 = r0; \
516 r2 = 4; \
517 r3 = 0; \
518 r4 = 0; \
519 r5 = 0; \
520 call %[bpf_csum_diff]; \
521 l0_%=: exit; \
522 " :
523 : __imm(bpf_csum_diff),
524 __imm(bpf_map_lookup_elem),
525 __imm_addr(map_array_wo)
526 : __clobber_all);
527 }
528
529 char _license[] SEC("license") = "GPL";
530