xref: /linux/tools/testing/selftests/bpf/progs/verifier_array_access.c (revision 4b132aacb0768ac1e652cf517097ea6f237214b9)
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)
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")
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")
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")
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
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
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
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")
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")
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
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)
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)
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
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")
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)
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)
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
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")
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