xref: /linux/tools/testing/selftests/bpf/progs/verifier_array_access.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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_%=:	exit;						\
372 "	:
373 	: __imm(bpf_csum_diff),
374 	  __imm(bpf_map_lookup_elem),
375 	  __imm_addr(map_array_ro)
376 	: __clobber_all);
377 }
378 
379 SEC("socket")
380 __description("invalid write map access into a read-only array 1")
381 __failure __msg("write into map forbidden")
382 __failure_unpriv
383 __naked void a_read_only_array_1_2(void)
384 {
385 	asm volatile ("					\
386 	r1 = 0;						\
387 	*(u64*)(r10 - 8) = r1;				\
388 	r2 = r10;					\
389 	r2 += -8;					\
390 	r1 = %[map_array_ro] ll;			\
391 	call %[bpf_map_lookup_elem];			\
392 	if r0 == 0 goto l0_%=;				\
393 	r1 = 42;					\
394 	*(u64*)(r0 + 0) = r1;				\
395 l0_%=:	exit;						\
396 "	:
397 	: __imm(bpf_map_lookup_elem),
398 	  __imm_addr(map_array_ro)
399 	: __clobber_all);
400 }
401 
402 SEC("tc")
403 __description("invalid write map access into a read-only array 2")
404 __failure __msg("write into map forbidden")
405 __naked void a_read_only_array_2_2(void)
406 {
407 	asm volatile ("					\
408 	r6 = r1;					\
409 	r1 = 0;						\
410 	*(u64*)(r10 - 8) = r1;				\
411 	r2 = r10;					\
412 	r2 += -8;					\
413 	r1 = %[map_array_ro] ll;			\
414 	call %[bpf_map_lookup_elem];			\
415 	if r0 == 0 goto l0_%=;				\
416 	r1 = r6;					\
417 	r2 = 0;						\
418 	r3 = r0;					\
419 	r4 = 8;						\
420 	call %[bpf_skb_load_bytes];			\
421 l0_%=:	exit;						\
422 "	:
423 	: __imm(bpf_map_lookup_elem),
424 	  __imm(bpf_skb_load_bytes),
425 	  __imm_addr(map_array_ro)
426 	: __clobber_all);
427 }
428 
429 SEC("socket")
430 __description("valid write map access into a write-only array 1")
431 __success __success_unpriv __retval(1)
432 __naked void a_write_only_array_1_1(void)
433 {
434 	asm volatile ("					\
435 	r1 = 0;						\
436 	*(u64*)(r10 - 8) = r1;				\
437 	r2 = r10;					\
438 	r2 += -8;					\
439 	r1 = %[map_array_wo] ll;			\
440 	call %[bpf_map_lookup_elem];			\
441 	if r0 == 0 goto l0_%=;				\
442 	r1 = 42;					\
443 	*(u64*)(r0 + 0) = r1;				\
444 l0_%=:	r0 = 1;						\
445 	exit;						\
446 "	:
447 	: __imm(bpf_map_lookup_elem),
448 	  __imm_addr(map_array_wo)
449 	: __clobber_all);
450 }
451 
452 SEC("tc")
453 __description("valid write map access into a write-only array 2")
454 __success __retval(0)
455 __naked void a_write_only_array_2_1(void)
456 {
457 	asm volatile ("					\
458 	r6 = r1;					\
459 	r1 = 0;						\
460 	*(u64*)(r10 - 8) = r1;				\
461 	r2 = r10;					\
462 	r2 += -8;					\
463 	r1 = %[map_array_wo] ll;			\
464 	call %[bpf_map_lookup_elem];			\
465 	if r0 == 0 goto l0_%=;				\
466 	r1 = r6;					\
467 	r2 = 0;						\
468 	r3 = r0;					\
469 	r4 = 8;						\
470 	call %[bpf_skb_load_bytes];			\
471 l0_%=:	exit;						\
472 "	:
473 	: __imm(bpf_map_lookup_elem),
474 	  __imm(bpf_skb_load_bytes),
475 	  __imm_addr(map_array_wo)
476 	: __clobber_all);
477 }
478 
479 SEC("socket")
480 __description("invalid read map access into a write-only array 1")
481 __failure __msg("read from map forbidden")
482 __failure_unpriv
483 __naked void a_write_only_array_1_2(void)
484 {
485 	asm volatile ("					\
486 	r1 = 0;						\
487 	*(u64*)(r10 - 8) = r1;				\
488 	r2 = r10;					\
489 	r2 += -8;					\
490 	r1 = %[map_array_wo] ll;			\
491 	call %[bpf_map_lookup_elem];			\
492 	if r0 == 0 goto l0_%=;				\
493 	r0 = *(u64*)(r0 + 0);				\
494 l0_%=:	exit;						\
495 "	:
496 	: __imm(bpf_map_lookup_elem),
497 	  __imm_addr(map_array_wo)
498 	: __clobber_all);
499 }
500 
501 SEC("tc")
502 __description("invalid read map access into a write-only array 2")
503 __failure __msg("read from map forbidden")
504 __naked void a_write_only_array_2_2(void)
505 {
506 	asm volatile ("					\
507 	r1 = 0;						\
508 	*(u64*)(r10 - 8) = r1;				\
509 	r2 = r10;					\
510 	r2 += -8;					\
511 	r1 = %[map_array_wo] ll;			\
512 	call %[bpf_map_lookup_elem];			\
513 	if r0 == 0 goto l0_%=;				\
514 	r1 = r0;					\
515 	r2 = 4;						\
516 	r3 = 0;						\
517 	r4 = 0;						\
518 	r5 = 0;						\
519 	call %[bpf_csum_diff];				\
520 l0_%=:	exit;						\
521 "	:
522 	: __imm(bpf_csum_diff),
523 	  __imm(bpf_map_lookup_elem),
524 	  __imm_addr(map_array_wo)
525 	: __clobber_all);
526 }
527 
528 char _license[] SEC("license") = "GPL";
529