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 | |