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
10struct test_val {
11 unsigned int index;
12 int foo[MAX_ENTRIES];
13};
14
15struct {
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
23struct {
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
31struct {
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
38SEC("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; \
54l0_%=: 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
62SEC("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; \
81l0_%=: 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
89SEC("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; \
109l0_%=: 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
118SEC("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; \
135l1_%=: w2 = %[max_entries]; \
136 if r2 s> r1 goto l2_%=; \
137 w1 = 0; \
138l2_%=: w1 <<= 2; \
139 r0 += r1; \
140 r1 = %[test_val_foo]; \
141 *(u64*)(r0 + 0) = r1; \
142l0_%=: 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
151SEC("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; \
167l0_%=: 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
176SEC("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; \
196l0_%=: 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
205SEC("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; \
226l0_%=: 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
234SEC("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; \
253l1_%=: w1 <<= 2; \
254 r0 += r1; \
255 r1 = %[test_val_foo]; \
256 *(u64*)(r0 + 0) = r1; \
257l0_%=: 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
266SEC("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; \
285l1_%=: w1 <<= 2; \
286 r0 += r1; \
287 r1 = %[test_val_foo]; \
288 *(u64*)(r0 + 0) = r1; \
289l0_%=: 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
298SEC("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]); \
323l0_%=: 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
331SEC("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); \
345l0_%=: exit; \
346" :
347 : __imm(bpf_map_lookup_elem),
348 __imm_addr(map_array_ro)
349 : __clobber_all);
350}
351
352SEC("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]; \
371l0_%=: 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
380SEC("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; \
396l0_%=: exit; \
397" :
398 : __imm(bpf_map_lookup_elem),
399 __imm_addr(map_array_ro)
400 : __clobber_all);
401}
402
403SEC("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]; \
422l0_%=: 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
430SEC("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; \
445l0_%=: r0 = 1; \
446 exit; \
447" :
448 : __imm(bpf_map_lookup_elem),
449 __imm_addr(map_array_wo)
450 : __clobber_all);
451}
452
453SEC("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]; \
472l0_%=: 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
480SEC("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); \
495l0_%=: exit; \
496" :
497 : __imm(bpf_map_lookup_elem),
498 __imm_addr(map_array_wo)
499 : __clobber_all);
500}
501
502SEC("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]; \
521l0_%=: exit; \
522" :
523 : __imm(bpf_csum_diff),
524 __imm(bpf_map_lookup_elem),
525 __imm_addr(map_array_wo)
526 : __clobber_all);
527}
528
529char _license[] SEC("license") = "GPL";
530

source code of linux/tools/testing/selftests/bpf/progs/verifier_array_access.c