1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ |
3 | |
4 | #include <stdbool.h> |
5 | #include <linux/bpf.h> |
6 | #include <bpf/bpf_helpers.h> |
7 | #include "bpf_misc.h" |
8 | #include "bpf_compiler.h" |
9 | |
10 | #define ARRAY_SIZE(x) (int)(sizeof(x) / sizeof((x)[0])) |
11 | |
12 | static volatile int zero = 0; |
13 | |
14 | int my_pid; |
15 | int arr[256]; |
16 | int small_arr[16] SEC(".data.small_arr" ); |
17 | |
18 | struct { |
19 | __uint(type, BPF_MAP_TYPE_HASH); |
20 | __uint(max_entries, 10); |
21 | __type(key, int); |
22 | __type(value, int); |
23 | } amap SEC(".maps" ); |
24 | |
25 | #ifdef REAL_TEST |
26 | #define MY_PID_GUARD() if (my_pid != (bpf_get_current_pid_tgid() >> 32)) return 0 |
27 | #else |
28 | #define MY_PID_GUARD() ({ }) |
29 | #endif |
30 | |
31 | SEC("?raw_tp" ) |
32 | __failure __msg("math between map_value pointer and register with unbounded min value is not allowed" ) |
33 | int iter_err_unsafe_c_loop(const void *ctx) |
34 | { |
35 | struct bpf_iter_num it; |
36 | int *v, i = zero; /* obscure initial value of i */ |
37 | |
38 | MY_PID_GUARD(); |
39 | |
40 | bpf_iter_num_new(&it, 0, 1000); |
41 | while ((v = bpf_iter_num_next(&it))) { |
42 | i++; |
43 | } |
44 | bpf_iter_num_destroy(&it); |
45 | |
46 | small_arr[i] = 123; /* invalid */ |
47 | |
48 | return 0; |
49 | } |
50 | |
51 | SEC("?raw_tp" ) |
52 | __failure __msg("unbounded memory access" ) |
53 | int iter_err_unsafe_asm_loop(const void *ctx) |
54 | { |
55 | struct bpf_iter_num it; |
56 | |
57 | MY_PID_GUARD(); |
58 | |
59 | asm volatile ( |
60 | "r6 = %[zero];" /* iteration counter */ |
61 | "r1 = %[it];" /* iterator state */ |
62 | "r2 = 0;" |
63 | "r3 = 1000;" |
64 | "r4 = 1;" |
65 | "call %[bpf_iter_num_new];" |
66 | "loop:" |
67 | "r1 = %[it];" |
68 | "call %[bpf_iter_num_next];" |
69 | "if r0 == 0 goto out;" |
70 | "r6 += 1;" |
71 | "goto loop;" |
72 | "out:" |
73 | "r1 = %[it];" |
74 | "call %[bpf_iter_num_destroy];" |
75 | "r1 = %[small_arr];" |
76 | "r2 = r6;" |
77 | "r2 <<= 2;" |
78 | "r1 += r2;" |
79 | "*(u32 *)(r1 + 0) = r6;" /* invalid */ |
80 | : |
81 | : [it]"r" (&it), |
82 | [small_arr]"r" (small_arr), |
83 | [zero]"r" (zero), |
84 | __imm(bpf_iter_num_new), |
85 | __imm(bpf_iter_num_next), |
86 | __imm(bpf_iter_num_destroy) |
87 | : __clobber_common, "r6" |
88 | ); |
89 | |
90 | return 0; |
91 | } |
92 | |
93 | SEC("raw_tp" ) |
94 | __success |
95 | int iter_while_loop(const void *ctx) |
96 | { |
97 | struct bpf_iter_num it; |
98 | int *v; |
99 | |
100 | MY_PID_GUARD(); |
101 | |
102 | bpf_iter_num_new(&it, 0, 3); |
103 | while ((v = bpf_iter_num_next(&it))) { |
104 | bpf_printk("ITER_BASIC: E1 VAL: v=%d" , *v); |
105 | } |
106 | bpf_iter_num_destroy(&it); |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | SEC("raw_tp" ) |
112 | __success |
113 | int iter_while_loop_auto_cleanup(const void *ctx) |
114 | { |
115 | __attribute__((cleanup(bpf_iter_num_destroy))) struct bpf_iter_num it; |
116 | int *v; |
117 | |
118 | MY_PID_GUARD(); |
119 | |
120 | bpf_iter_num_new(&it, 0, 3); |
121 | while ((v = bpf_iter_num_next(&it))) { |
122 | bpf_printk("ITER_BASIC: E1 VAL: v=%d" , *v); |
123 | } |
124 | /* (!) no explicit bpf_iter_num_destroy() */ |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | SEC("raw_tp" ) |
130 | __success |
131 | int iter_for_loop(const void *ctx) |
132 | { |
133 | struct bpf_iter_num it; |
134 | int *v; |
135 | |
136 | MY_PID_GUARD(); |
137 | |
138 | bpf_iter_num_new(&it, 5, 10); |
139 | for (v = bpf_iter_num_next(&it); v; v = bpf_iter_num_next(&it)) { |
140 | bpf_printk("ITER_BASIC: E2 VAL: v=%d" , *v); |
141 | } |
142 | bpf_iter_num_destroy(&it); |
143 | |
144 | return 0; |
145 | } |
146 | |
147 | SEC("raw_tp" ) |
148 | __success |
149 | int iter_bpf_for_each_macro(const void *ctx) |
150 | { |
151 | int *v; |
152 | |
153 | MY_PID_GUARD(); |
154 | |
155 | bpf_for_each(num, v, 5, 10) { |
156 | bpf_printk("ITER_BASIC: E2 VAL: v=%d" , *v); |
157 | } |
158 | |
159 | return 0; |
160 | } |
161 | |
162 | SEC("raw_tp" ) |
163 | __success |
164 | int iter_bpf_for_macro(const void *ctx) |
165 | { |
166 | int i; |
167 | |
168 | MY_PID_GUARD(); |
169 | |
170 | bpf_for(i, 5, 10) { |
171 | bpf_printk("ITER_BASIC: E2 VAL: v=%d" , i); |
172 | } |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | SEC("raw_tp" ) |
178 | __success |
179 | int iter_pragma_unroll_loop(const void *ctx) |
180 | { |
181 | struct bpf_iter_num it; |
182 | int *v, i; |
183 | |
184 | MY_PID_GUARD(); |
185 | |
186 | bpf_iter_num_new(&it, 0, 2); |
187 | __pragma_loop_no_unroll |
188 | for (i = 0; i < 3; i++) { |
189 | v = bpf_iter_num_next(&it); |
190 | bpf_printk("ITER_BASIC: E3 VAL: i=%d v=%d" , i, v ? *v : -1); |
191 | } |
192 | bpf_iter_num_destroy(&it); |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | SEC("raw_tp" ) |
198 | __success |
199 | int iter_manual_unroll_loop(const void *ctx) |
200 | { |
201 | struct bpf_iter_num it; |
202 | int *v; |
203 | |
204 | MY_PID_GUARD(); |
205 | |
206 | bpf_iter_num_new(&it, 100, 200); |
207 | v = bpf_iter_num_next(&it); |
208 | bpf_printk("ITER_BASIC: E4 VAL: v=%d" , v ? *v : -1); |
209 | v = bpf_iter_num_next(&it); |
210 | bpf_printk("ITER_BASIC: E4 VAL: v=%d" , v ? *v : -1); |
211 | v = bpf_iter_num_next(&it); |
212 | bpf_printk("ITER_BASIC: E4 VAL: v=%d" , v ? *v : -1); |
213 | v = bpf_iter_num_next(&it); |
214 | bpf_printk("ITER_BASIC: E4 VAL: v=%d\n" , v ? *v : -1); |
215 | bpf_iter_num_destroy(&it); |
216 | |
217 | return 0; |
218 | } |
219 | |
220 | SEC("raw_tp" ) |
221 | __success |
222 | int iter_multiple_sequential_loops(const void *ctx) |
223 | { |
224 | struct bpf_iter_num it; |
225 | int *v, i; |
226 | |
227 | MY_PID_GUARD(); |
228 | |
229 | bpf_iter_num_new(&it, 0, 3); |
230 | while ((v = bpf_iter_num_next(&it))) { |
231 | bpf_printk("ITER_BASIC: E1 VAL: v=%d" , *v); |
232 | } |
233 | bpf_iter_num_destroy(&it); |
234 | |
235 | bpf_iter_num_new(&it, 5, 10); |
236 | for (v = bpf_iter_num_next(&it); v; v = bpf_iter_num_next(&it)) { |
237 | bpf_printk("ITER_BASIC: E2 VAL: v=%d" , *v); |
238 | } |
239 | bpf_iter_num_destroy(&it); |
240 | |
241 | bpf_iter_num_new(&it, 0, 2); |
242 | __pragma_loop_no_unroll |
243 | for (i = 0; i < 3; i++) { |
244 | v = bpf_iter_num_next(&it); |
245 | bpf_printk("ITER_BASIC: E3 VAL: i=%d v=%d" , i, v ? *v : -1); |
246 | } |
247 | bpf_iter_num_destroy(&it); |
248 | |
249 | bpf_iter_num_new(&it, 100, 200); |
250 | v = bpf_iter_num_next(&it); |
251 | bpf_printk("ITER_BASIC: E4 VAL: v=%d" , v ? *v : -1); |
252 | v = bpf_iter_num_next(&it); |
253 | bpf_printk("ITER_BASIC: E4 VAL: v=%d" , v ? *v : -1); |
254 | v = bpf_iter_num_next(&it); |
255 | bpf_printk("ITER_BASIC: E4 VAL: v=%d" , v ? *v : -1); |
256 | v = bpf_iter_num_next(&it); |
257 | bpf_printk("ITER_BASIC: E4 VAL: v=%d\n" , v ? *v : -1); |
258 | bpf_iter_num_destroy(&it); |
259 | |
260 | return 0; |
261 | } |
262 | |
263 | SEC("raw_tp" ) |
264 | __success |
265 | int iter_limit_cond_break_loop(const void *ctx) |
266 | { |
267 | struct bpf_iter_num it; |
268 | int *v, i = 0, sum = 0; |
269 | |
270 | MY_PID_GUARD(); |
271 | |
272 | bpf_iter_num_new(&it, 0, 10); |
273 | while ((v = bpf_iter_num_next(&it))) { |
274 | bpf_printk("ITER_SIMPLE: i=%d v=%d" , i, *v); |
275 | sum += *v; |
276 | |
277 | i++; |
278 | if (i > 3) |
279 | break; |
280 | } |
281 | bpf_iter_num_destroy(&it); |
282 | |
283 | bpf_printk("ITER_SIMPLE: sum=%d\n" , sum); |
284 | |
285 | return 0; |
286 | } |
287 | |
288 | SEC("raw_tp" ) |
289 | __success |
290 | int iter_obfuscate_counter(const void *ctx) |
291 | { |
292 | struct bpf_iter_num it; |
293 | int *v, sum = 0; |
294 | /* Make i's initial value unknowable for verifier to prevent it from |
295 | * pruning if/else branch inside the loop body and marking i as precise. |
296 | */ |
297 | int i = zero; |
298 | |
299 | MY_PID_GUARD(); |
300 | |
301 | bpf_iter_num_new(&it, 0, 10); |
302 | while ((v = bpf_iter_num_next(&it))) { |
303 | int x; |
304 | |
305 | i += 1; |
306 | |
307 | /* If we initialized i as `int i = 0;` above, verifier would |
308 | * track that i becomes 1 on first iteration after increment |
309 | * above, and here verifier would eagerly prune else branch |
310 | * and mark i as precise, ruining open-coded iterator logic |
311 | * completely, as each next iteration would have a different |
312 | * *precise* value of i, and thus there would be no |
313 | * convergence of state. This would result in reaching maximum |
314 | * instruction limit, no matter what the limit is. |
315 | */ |
316 | if (i == 1) |
317 | x = 123; |
318 | else |
319 | x = i * 3 + 1; |
320 | |
321 | bpf_printk("ITER_OBFUSCATE_COUNTER: i=%d v=%d x=%d" , i, *v, x); |
322 | |
323 | sum += x; |
324 | } |
325 | bpf_iter_num_destroy(&it); |
326 | |
327 | bpf_printk("ITER_OBFUSCATE_COUNTER: sum=%d\n" , sum); |
328 | |
329 | return 0; |
330 | } |
331 | |
332 | SEC("raw_tp" ) |
333 | __success |
334 | int iter_search_loop(const void *ctx) |
335 | { |
336 | struct bpf_iter_num it; |
337 | int *v, *elem = NULL; |
338 | bool found = false; |
339 | |
340 | MY_PID_GUARD(); |
341 | |
342 | bpf_iter_num_new(&it, 0, 10); |
343 | |
344 | while ((v = bpf_iter_num_next(&it))) { |
345 | bpf_printk("ITER_SEARCH_LOOP: v=%d" , *v); |
346 | |
347 | if (*v == 2) { |
348 | found = true; |
349 | elem = v; |
350 | barrier_var(elem); |
351 | } |
352 | } |
353 | |
354 | /* should fail to verify if bpf_iter_num_destroy() is here */ |
355 | |
356 | if (found) |
357 | /* here found element will be wrong, we should have copied |
358 | * value to a variable, but here we want to make sure we can |
359 | * access memory after the loop anyways |
360 | */ |
361 | bpf_printk("ITER_SEARCH_LOOP: FOUND IT = %d!\n" , *elem); |
362 | else |
363 | bpf_printk("ITER_SEARCH_LOOP: NOT FOUND IT!\n" ); |
364 | |
365 | bpf_iter_num_destroy(&it); |
366 | |
367 | return 0; |
368 | } |
369 | |
370 | SEC("raw_tp" ) |
371 | __success |
372 | int iter_array_fill(const void *ctx) |
373 | { |
374 | int sum, i; |
375 | |
376 | MY_PID_GUARD(); |
377 | |
378 | bpf_for(i, 0, ARRAY_SIZE(arr)) { |
379 | arr[i] = i * 2; |
380 | } |
381 | |
382 | sum = 0; |
383 | bpf_for(i, 0, ARRAY_SIZE(arr)) { |
384 | sum += arr[i]; |
385 | } |
386 | |
387 | bpf_printk("ITER_ARRAY_FILL: sum=%d (should be %d)\n" , sum, 255 * 256); |
388 | |
389 | return 0; |
390 | } |
391 | |
392 | static int arr2d[4][5]; |
393 | static int arr2d_row_sums[4]; |
394 | static int arr2d_col_sums[5]; |
395 | |
396 | SEC("raw_tp" ) |
397 | __success |
398 | int iter_nested_iters(const void *ctx) |
399 | { |
400 | int sum, row, col; |
401 | |
402 | MY_PID_GUARD(); |
403 | |
404 | bpf_for(row, 0, ARRAY_SIZE(arr2d)) { |
405 | bpf_for( col, 0, ARRAY_SIZE(arr2d[0])) { |
406 | arr2d[row][col] = row * col; |
407 | } |
408 | } |
409 | |
410 | /* zero-initialize sums */ |
411 | sum = 0; |
412 | bpf_for(row, 0, ARRAY_SIZE(arr2d)) { |
413 | arr2d_row_sums[row] = 0; |
414 | } |
415 | bpf_for(col, 0, ARRAY_SIZE(arr2d[0])) { |
416 | arr2d_col_sums[col] = 0; |
417 | } |
418 | |
419 | /* calculate sums */ |
420 | bpf_for(row, 0, ARRAY_SIZE(arr2d)) { |
421 | bpf_for(col, 0, ARRAY_SIZE(arr2d[0])) { |
422 | sum += arr2d[row][col]; |
423 | arr2d_row_sums[row] += arr2d[row][col]; |
424 | arr2d_col_sums[col] += arr2d[row][col]; |
425 | } |
426 | } |
427 | |
428 | bpf_printk("ITER_NESTED_ITERS: total sum=%d" , sum); |
429 | bpf_for(row, 0, ARRAY_SIZE(arr2d)) { |
430 | bpf_printk("ITER_NESTED_ITERS: row #%d sum=%d" , row, arr2d_row_sums[row]); |
431 | } |
432 | bpf_for(col, 0, ARRAY_SIZE(arr2d[0])) { |
433 | bpf_printk("ITER_NESTED_ITERS: col #%d sum=%d%s" , |
434 | col, arr2d_col_sums[col], |
435 | col == ARRAY_SIZE(arr2d[0]) - 1 ? "\n" : "" ); |
436 | } |
437 | |
438 | return 0; |
439 | } |
440 | |
441 | SEC("raw_tp" ) |
442 | __success |
443 | int iter_nested_deeply_iters(const void *ctx) |
444 | { |
445 | int sum = 0; |
446 | |
447 | MY_PID_GUARD(); |
448 | |
449 | bpf_repeat(10) { |
450 | bpf_repeat(10) { |
451 | bpf_repeat(10) { |
452 | bpf_repeat(10) { |
453 | bpf_repeat(10) { |
454 | sum += 1; |
455 | } |
456 | } |
457 | } |
458 | } |
459 | /* validate that we can break from inside bpf_repeat() */ |
460 | break; |
461 | } |
462 | |
463 | return sum; |
464 | } |
465 | |
466 | static __noinline void fill_inner_dimension(int row) |
467 | { |
468 | int col; |
469 | |
470 | bpf_for(col, 0, ARRAY_SIZE(arr2d[0])) { |
471 | arr2d[row][col] = row * col; |
472 | } |
473 | } |
474 | |
475 | static __noinline int sum_inner_dimension(int row) |
476 | { |
477 | int sum = 0, col; |
478 | |
479 | bpf_for(col, 0, ARRAY_SIZE(arr2d[0])) { |
480 | sum += arr2d[row][col]; |
481 | arr2d_row_sums[row] += arr2d[row][col]; |
482 | arr2d_col_sums[col] += arr2d[row][col]; |
483 | } |
484 | |
485 | return sum; |
486 | } |
487 | |
488 | SEC("raw_tp" ) |
489 | __success |
490 | int iter_subprog_iters(const void *ctx) |
491 | { |
492 | int sum, row, col; |
493 | |
494 | MY_PID_GUARD(); |
495 | |
496 | bpf_for(row, 0, ARRAY_SIZE(arr2d)) { |
497 | fill_inner_dimension(row); |
498 | } |
499 | |
500 | /* zero-initialize sums */ |
501 | sum = 0; |
502 | bpf_for(row, 0, ARRAY_SIZE(arr2d)) { |
503 | arr2d_row_sums[row] = 0; |
504 | } |
505 | bpf_for(col, 0, ARRAY_SIZE(arr2d[0])) { |
506 | arr2d_col_sums[col] = 0; |
507 | } |
508 | |
509 | /* calculate sums */ |
510 | bpf_for(row, 0, ARRAY_SIZE(arr2d)) { |
511 | sum += sum_inner_dimension(row); |
512 | } |
513 | |
514 | bpf_printk("ITER_SUBPROG_ITERS: total sum=%d" , sum); |
515 | bpf_for(row, 0, ARRAY_SIZE(arr2d)) { |
516 | bpf_printk("ITER_SUBPROG_ITERS: row #%d sum=%d" , |
517 | row, arr2d_row_sums[row]); |
518 | } |
519 | bpf_for(col, 0, ARRAY_SIZE(arr2d[0])) { |
520 | bpf_printk("ITER_SUBPROG_ITERS: col #%d sum=%d%s" , |
521 | col, arr2d_col_sums[col], |
522 | col == ARRAY_SIZE(arr2d[0]) - 1 ? "\n" : "" ); |
523 | } |
524 | |
525 | return 0; |
526 | } |
527 | |
528 | struct { |
529 | __uint(type, BPF_MAP_TYPE_ARRAY); |
530 | __type(key, int); |
531 | __type(value, int); |
532 | __uint(max_entries, 1000); |
533 | } arr_map SEC(".maps" ); |
534 | |
535 | SEC("?raw_tp" ) |
536 | __failure __msg("invalid mem access 'scalar'" ) |
537 | int iter_err_too_permissive1(const void *ctx) |
538 | { |
539 | int *map_val = NULL; |
540 | int key = 0; |
541 | |
542 | MY_PID_GUARD(); |
543 | |
544 | map_val = bpf_map_lookup_elem(&arr_map, &key); |
545 | if (!map_val) |
546 | return 0; |
547 | |
548 | bpf_repeat(1000000) { |
549 | map_val = NULL; |
550 | } |
551 | |
552 | *map_val = 123; |
553 | |
554 | return 0; |
555 | } |
556 | |
557 | SEC("?raw_tp" ) |
558 | __failure __msg("invalid mem access 'map_value_or_null'" ) |
559 | int iter_err_too_permissive2(const void *ctx) |
560 | { |
561 | int *map_val = NULL; |
562 | int key = 0; |
563 | |
564 | MY_PID_GUARD(); |
565 | |
566 | map_val = bpf_map_lookup_elem(&arr_map, &key); |
567 | if (!map_val) |
568 | return 0; |
569 | |
570 | bpf_repeat(1000000) { |
571 | map_val = bpf_map_lookup_elem(&arr_map, &key); |
572 | } |
573 | |
574 | *map_val = 123; |
575 | |
576 | return 0; |
577 | } |
578 | |
579 | SEC("?raw_tp" ) |
580 | __failure __msg("invalid mem access 'map_value_or_null'" ) |
581 | int iter_err_too_permissive3(const void *ctx) |
582 | { |
583 | int *map_val = NULL; |
584 | int key = 0; |
585 | bool found = false; |
586 | |
587 | MY_PID_GUARD(); |
588 | |
589 | bpf_repeat(1000000) { |
590 | map_val = bpf_map_lookup_elem(&arr_map, &key); |
591 | found = true; |
592 | } |
593 | |
594 | if (found) |
595 | *map_val = 123; |
596 | |
597 | return 0; |
598 | } |
599 | |
600 | SEC("raw_tp" ) |
601 | __success |
602 | int iter_tricky_but_fine(const void *ctx) |
603 | { |
604 | int *map_val = NULL; |
605 | int key = 0; |
606 | bool found = false; |
607 | |
608 | MY_PID_GUARD(); |
609 | |
610 | bpf_repeat(1000000) { |
611 | map_val = bpf_map_lookup_elem(&arr_map, &key); |
612 | if (map_val) { |
613 | found = true; |
614 | break; |
615 | } |
616 | } |
617 | |
618 | if (found) |
619 | *map_val = 123; |
620 | |
621 | return 0; |
622 | } |
623 | |
624 | #define __bpf_memzero(p, sz) bpf_probe_read_kernel((p), (sz), 0) |
625 | |
626 | SEC("raw_tp" ) |
627 | __success |
628 | int iter_stack_array_loop(const void *ctx) |
629 | { |
630 | long arr1[16], arr2[16], sum = 0; |
631 | int i; |
632 | |
633 | MY_PID_GUARD(); |
634 | |
635 | /* zero-init arr1 and arr2 in such a way that verifier doesn't know |
636 | * it's all zeros; if we don't do that, we'll make BPF verifier track |
637 | * all combination of zero/non-zero stack slots for arr1/arr2, which |
638 | * will lead to O(2^(ARRAY_SIZE(arr1)+ARRAY_SIZE(arr2))) different |
639 | * states |
640 | */ |
641 | __bpf_memzero(arr1, sizeof(arr1)); |
642 | __bpf_memzero(arr2, sizeof(arr1)); |
643 | |
644 | /* validate that we can break and continue when using bpf_for() */ |
645 | bpf_for(i, 0, ARRAY_SIZE(arr1)) { |
646 | if (i & 1) { |
647 | arr1[i] = i; |
648 | continue; |
649 | } else { |
650 | arr2[i] = i; |
651 | break; |
652 | } |
653 | } |
654 | |
655 | bpf_for(i, 0, ARRAY_SIZE(arr1)) { |
656 | sum += arr1[i] + arr2[i]; |
657 | } |
658 | |
659 | return sum; |
660 | } |
661 | |
662 | static __noinline void fill(struct bpf_iter_num *it, int *arr, __u32 n, int mul) |
663 | { |
664 | int *t, i; |
665 | |
666 | while ((t = bpf_iter_num_next(it))) { |
667 | i = *t; |
668 | if (i >= n) |
669 | break; |
670 | arr[i] = i * mul; |
671 | } |
672 | } |
673 | |
674 | static __noinline int sum(struct bpf_iter_num *it, int *arr, __u32 n) |
675 | { |
676 | int *t, i, sum = 0;; |
677 | |
678 | while ((t = bpf_iter_num_next(it))) { |
679 | i = *t; |
680 | if ((__u32)i >= n) |
681 | break; |
682 | sum += arr[i]; |
683 | } |
684 | |
685 | return sum; |
686 | } |
687 | |
688 | SEC("raw_tp" ) |
689 | __success |
690 | int iter_pass_iter_ptr_to_subprog(const void *ctx) |
691 | { |
692 | int arr1[16], arr2[32]; |
693 | struct bpf_iter_num it; |
694 | int n, sum1, sum2; |
695 | |
696 | MY_PID_GUARD(); |
697 | |
698 | /* fill arr1 */ |
699 | n = ARRAY_SIZE(arr1); |
700 | bpf_iter_num_new(&it, 0, n); |
701 | fill(&it, arr1, n, 2); |
702 | bpf_iter_num_destroy(&it); |
703 | |
704 | /* fill arr2 */ |
705 | n = ARRAY_SIZE(arr2); |
706 | bpf_iter_num_new(&it, 0, n); |
707 | fill(&it, arr2, n, 10); |
708 | bpf_iter_num_destroy(&it); |
709 | |
710 | /* sum arr1 */ |
711 | n = ARRAY_SIZE(arr1); |
712 | bpf_iter_num_new(&it, 0, n); |
713 | sum1 = sum(&it, arr1, n); |
714 | bpf_iter_num_destroy(&it); |
715 | |
716 | /* sum arr2 */ |
717 | n = ARRAY_SIZE(arr2); |
718 | bpf_iter_num_new(&it, 0, n); |
719 | sum2 = sum(&it, arr2, n); |
720 | bpf_iter_num_destroy(&it); |
721 | |
722 | bpf_printk("sum1=%d, sum2=%d" , sum1, sum2); |
723 | |
724 | return 0; |
725 | } |
726 | |
727 | SEC("?raw_tp" ) |
728 | __failure |
729 | __msg("R1 type=scalar expected=fp" ) |
730 | __naked int delayed_read_mark(void) |
731 | { |
732 | /* This is equivalent to C program below. |
733 | * The call to bpf_iter_num_next() is reachable with r7 values &fp[-16] and 0xdead. |
734 | * State with r7=&fp[-16] is visited first and follows r6 != 42 ... continue branch. |
735 | * At this point iterator next() call is reached with r7 that has no read mark. |
736 | * Loop body with r7=0xdead would only be visited if verifier would decide to continue |
737 | * with second loop iteration. Absence of read mark on r7 might affect state |
738 | * equivalent logic used for iterator convergence tracking. |
739 | * |
740 | * r7 = &fp[-16] |
741 | * fp[-16] = 0 |
742 | * r6 = bpf_get_prandom_u32() |
743 | * bpf_iter_num_new(&fp[-8], 0, 10) |
744 | * while (bpf_iter_num_next(&fp[-8])) { |
745 | * r6++ |
746 | * if (r6 != 42) { |
747 | * r7 = 0xdead |
748 | * continue; |
749 | * } |
750 | * bpf_probe_read_user(r7, 8, 0xdeadbeef); // this is not safe |
751 | * } |
752 | * bpf_iter_num_destroy(&fp[-8]) |
753 | * return 0 |
754 | */ |
755 | asm volatile ( |
756 | "r7 = r10;" |
757 | "r7 += -16;" |
758 | "r0 = 0;" |
759 | "*(u64 *)(r7 + 0) = r0;" |
760 | "call %[bpf_get_prandom_u32];" |
761 | "r6 = r0;" |
762 | "r1 = r10;" |
763 | "r1 += -8;" |
764 | "r2 = 0;" |
765 | "r3 = 10;" |
766 | "call %[bpf_iter_num_new];" |
767 | "1:" |
768 | "r1 = r10;" |
769 | "r1 += -8;" |
770 | "call %[bpf_iter_num_next];" |
771 | "if r0 == 0 goto 2f;" |
772 | "r6 += 1;" |
773 | "if r6 != 42 goto 3f;" |
774 | "r7 = 0xdead;" |
775 | "goto 1b;" |
776 | "3:" |
777 | "r1 = r7;" |
778 | "r2 = 8;" |
779 | "r3 = 0xdeadbeef;" |
780 | "call %[bpf_probe_read_user];" |
781 | "goto 1b;" |
782 | "2:" |
783 | "r1 = r10;" |
784 | "r1 += -8;" |
785 | "call %[bpf_iter_num_destroy];" |
786 | "r0 = 0;" |
787 | "exit;" |
788 | : |
789 | : __imm(bpf_get_prandom_u32), |
790 | __imm(bpf_iter_num_new), |
791 | __imm(bpf_iter_num_next), |
792 | __imm(bpf_iter_num_destroy), |
793 | __imm(bpf_probe_read_user) |
794 | : __clobber_all |
795 | ); |
796 | } |
797 | |
798 | SEC("?raw_tp" ) |
799 | __failure |
800 | __msg("math between fp pointer and register with unbounded" ) |
801 | __naked int delayed_precision_mark(void) |
802 | { |
803 | /* This is equivalent to C program below. |
804 | * The test is similar to delayed_iter_mark but verifies that incomplete |
805 | * precision don't fool verifier. |
806 | * The call to bpf_iter_num_next() is reachable with r7 values -16 and -32. |
807 | * State with r7=-16 is visited first and follows r6 != 42 ... continue branch. |
808 | * At this point iterator next() call is reached with r7 that has no read |
809 | * and precision marks. |
810 | * Loop body with r7=-32 would only be visited if verifier would decide to continue |
811 | * with second loop iteration. Absence of precision mark on r7 might affect state |
812 | * equivalent logic used for iterator convergence tracking. |
813 | * |
814 | * r8 = 0 |
815 | * fp[-16] = 0 |
816 | * r7 = -16 |
817 | * r6 = bpf_get_prandom_u32() |
818 | * bpf_iter_num_new(&fp[-8], 0, 10) |
819 | * while (bpf_iter_num_next(&fp[-8])) { |
820 | * if (r6 != 42) { |
821 | * r7 = -32 |
822 | * r6 = bpf_get_prandom_u32() |
823 | * continue; |
824 | * } |
825 | * r0 = r10 |
826 | * r0 += r7 |
827 | * r8 = *(u64 *)(r0 + 0) // this is not safe |
828 | * r6 = bpf_get_prandom_u32() |
829 | * } |
830 | * bpf_iter_num_destroy(&fp[-8]) |
831 | * return r8 |
832 | */ |
833 | asm volatile ( |
834 | "r8 = 0;" |
835 | "*(u64 *)(r10 - 16) = r8;" |
836 | "r7 = -16;" |
837 | "call %[bpf_get_prandom_u32];" |
838 | "r6 = r0;" |
839 | "r1 = r10;" |
840 | "r1 += -8;" |
841 | "r2 = 0;" |
842 | "r3 = 10;" |
843 | "call %[bpf_iter_num_new];" |
844 | "1:" |
845 | "r1 = r10;" |
846 | "r1 += -8;\n" |
847 | "call %[bpf_iter_num_next];" |
848 | "if r0 == 0 goto 2f;" |
849 | "if r6 != 42 goto 3f;" |
850 | "r7 = -33;" |
851 | "call %[bpf_get_prandom_u32];" |
852 | "r6 = r0;" |
853 | "goto 1b;\n" |
854 | "3:" |
855 | "r0 = r10;" |
856 | "r0 += r7;" |
857 | "r8 = *(u64 *)(r0 + 0);" |
858 | "call %[bpf_get_prandom_u32];" |
859 | "r6 = r0;" |
860 | "goto 1b;\n" |
861 | "2:" |
862 | "r1 = r10;" |
863 | "r1 += -8;" |
864 | "call %[bpf_iter_num_destroy];" |
865 | "r0 = r8;" |
866 | "exit;" |
867 | : |
868 | : __imm(bpf_get_prandom_u32), |
869 | __imm(bpf_iter_num_new), |
870 | __imm(bpf_iter_num_next), |
871 | __imm(bpf_iter_num_destroy), |
872 | __imm(bpf_probe_read_user) |
873 | : __clobber_all |
874 | ); |
875 | } |
876 | |
877 | SEC("?raw_tp" ) |
878 | __failure |
879 | __msg("math between fp pointer and register with unbounded" ) |
880 | __flag(BPF_F_TEST_STATE_FREQ) |
881 | __naked int loop_state_deps1(void) |
882 | { |
883 | /* This is equivalent to C program below. |
884 | * |
885 | * The case turns out to be tricky in a sense that: |
886 | * - states with c=-25 are explored only on a second iteration |
887 | * of the outer loop; |
888 | * - states with read+precise mark on c are explored only on |
889 | * second iteration of the inner loop and in a state which |
890 | * is pushed to states stack first. |
891 | * |
892 | * Depending on the details of iterator convergence logic |
893 | * verifier might stop states traversal too early and miss |
894 | * unsafe c=-25 memory access. |
895 | * |
896 | * j = iter_new(); // fp[-16] |
897 | * a = 0; // r6 |
898 | * b = 0; // r7 |
899 | * c = -24; // r8 |
900 | * while (iter_next(j)) { |
901 | * i = iter_new(); // fp[-8] |
902 | * a = 0; // r6 |
903 | * b = 0; // r7 |
904 | * while (iter_next(i)) { |
905 | * if (a == 1) { |
906 | * a = 0; |
907 | * b = 1; |
908 | * } else if (a == 0) { |
909 | * a = 1; |
910 | * if (random() == 42) |
911 | * continue; |
912 | * if (b == 1) { |
913 | * *(r10 + c) = 7; // this is not safe |
914 | * iter_destroy(i); |
915 | * iter_destroy(j); |
916 | * return; |
917 | * } |
918 | * } |
919 | * } |
920 | * iter_destroy(i); |
921 | * a = 0; |
922 | * b = 0; |
923 | * c = -25; |
924 | * } |
925 | * iter_destroy(j); |
926 | * return; |
927 | */ |
928 | asm volatile ( |
929 | "r1 = r10;" |
930 | "r1 += -16;" |
931 | "r2 = 0;" |
932 | "r3 = 10;" |
933 | "call %[bpf_iter_num_new];" |
934 | "r6 = 0;" |
935 | "r7 = 0;" |
936 | "r8 = -24;" |
937 | "j_loop_%=:" |
938 | "r1 = r10;" |
939 | "r1 += -16;" |
940 | "call %[bpf_iter_num_next];" |
941 | "if r0 == 0 goto j_loop_end_%=;" |
942 | "r1 = r10;" |
943 | "r1 += -8;" |
944 | "r2 = 0;" |
945 | "r3 = 10;" |
946 | "call %[bpf_iter_num_new];" |
947 | "r6 = 0;" |
948 | "r7 = 0;" |
949 | "i_loop_%=:" |
950 | "r1 = r10;" |
951 | "r1 += -8;" |
952 | "call %[bpf_iter_num_next];" |
953 | "if r0 == 0 goto i_loop_end_%=;" |
954 | "check_one_r6_%=:" |
955 | "if r6 != 1 goto check_zero_r6_%=;" |
956 | "r6 = 0;" |
957 | "r7 = 1;" |
958 | "goto i_loop_%=;" |
959 | "check_zero_r6_%=:" |
960 | "if r6 != 0 goto i_loop_%=;" |
961 | "r6 = 1;" |
962 | "call %[bpf_get_prandom_u32];" |
963 | "if r0 != 42 goto check_one_r7_%=;" |
964 | "goto i_loop_%=;" |
965 | "check_one_r7_%=:" |
966 | "if r7 != 1 goto i_loop_%=;" |
967 | "r0 = r10;" |
968 | "r0 += r8;" |
969 | "r1 = 7;" |
970 | "*(u64 *)(r0 + 0) = r1;" |
971 | "r1 = r10;" |
972 | "r1 += -8;" |
973 | "call %[bpf_iter_num_destroy];" |
974 | "r1 = r10;" |
975 | "r1 += -16;" |
976 | "call %[bpf_iter_num_destroy];" |
977 | "r0 = 0;" |
978 | "exit;" |
979 | "i_loop_end_%=:" |
980 | "r1 = r10;" |
981 | "r1 += -8;" |
982 | "call %[bpf_iter_num_destroy];" |
983 | "r6 = 0;" |
984 | "r7 = 0;" |
985 | "r8 = -25;" |
986 | "goto j_loop_%=;" |
987 | "j_loop_end_%=:" |
988 | "r1 = r10;" |
989 | "r1 += -16;" |
990 | "call %[bpf_iter_num_destroy];" |
991 | "r0 = 0;" |
992 | "exit;" |
993 | : |
994 | : __imm(bpf_get_prandom_u32), |
995 | __imm(bpf_iter_num_new), |
996 | __imm(bpf_iter_num_next), |
997 | __imm(bpf_iter_num_destroy) |
998 | : __clobber_all |
999 | ); |
1000 | } |
1001 | |
1002 | SEC("?raw_tp" ) |
1003 | __failure |
1004 | __msg("math between fp pointer and register with unbounded" ) |
1005 | __flag(BPF_F_TEST_STATE_FREQ) |
1006 | __naked int loop_state_deps2(void) |
1007 | { |
1008 | /* This is equivalent to C program below. |
1009 | * |
1010 | * The case turns out to be tricky in a sense that: |
1011 | * - states with read+precise mark on c are explored only on a second |
1012 | * iteration of the first inner loop and in a state which is pushed to |
1013 | * states stack first. |
1014 | * - states with c=-25 are explored only on a second iteration of the |
1015 | * second inner loop and in a state which is pushed to states stack |
1016 | * first. |
1017 | * |
1018 | * Depending on the details of iterator convergence logic |
1019 | * verifier might stop states traversal too early and miss |
1020 | * unsafe c=-25 memory access. |
1021 | * |
1022 | * j = iter_new(); // fp[-16] |
1023 | * a = 0; // r6 |
1024 | * b = 0; // r7 |
1025 | * c = -24; // r8 |
1026 | * while (iter_next(j)) { |
1027 | * i = iter_new(); // fp[-8] |
1028 | * a = 0; // r6 |
1029 | * b = 0; // r7 |
1030 | * while (iter_next(i)) { |
1031 | * if (a == 1) { |
1032 | * a = 0; |
1033 | * b = 1; |
1034 | * } else if (a == 0) { |
1035 | * a = 1; |
1036 | * if (random() == 42) |
1037 | * continue; |
1038 | * if (b == 1) { |
1039 | * *(r10 + c) = 7; // this is not safe |
1040 | * iter_destroy(i); |
1041 | * iter_destroy(j); |
1042 | * return; |
1043 | * } |
1044 | * } |
1045 | * } |
1046 | * iter_destroy(i); |
1047 | * i = iter_new(); // fp[-8] |
1048 | * a = 0; // r6 |
1049 | * b = 0; // r7 |
1050 | * while (iter_next(i)) { |
1051 | * if (a == 1) { |
1052 | * a = 0; |
1053 | * b = 1; |
1054 | * } else if (a == 0) { |
1055 | * a = 1; |
1056 | * if (random() == 42) |
1057 | * continue; |
1058 | * if (b == 1) { |
1059 | * a = 0; |
1060 | * c = -25; |
1061 | * } |
1062 | * } |
1063 | * } |
1064 | * iter_destroy(i); |
1065 | * } |
1066 | * iter_destroy(j); |
1067 | * return; |
1068 | */ |
1069 | asm volatile ( |
1070 | "r1 = r10;" |
1071 | "r1 += -16;" |
1072 | "r2 = 0;" |
1073 | "r3 = 10;" |
1074 | "call %[bpf_iter_num_new];" |
1075 | "r6 = 0;" |
1076 | "r7 = 0;" |
1077 | "r8 = -24;" |
1078 | "j_loop_%=:" |
1079 | "r1 = r10;" |
1080 | "r1 += -16;" |
1081 | "call %[bpf_iter_num_next];" |
1082 | "if r0 == 0 goto j_loop_end_%=;" |
1083 | |
1084 | /* first inner loop */ |
1085 | "r1 = r10;" |
1086 | "r1 += -8;" |
1087 | "r2 = 0;" |
1088 | "r3 = 10;" |
1089 | "call %[bpf_iter_num_new];" |
1090 | "r6 = 0;" |
1091 | "r7 = 0;" |
1092 | "i_loop_%=:" |
1093 | "r1 = r10;" |
1094 | "r1 += -8;" |
1095 | "call %[bpf_iter_num_next];" |
1096 | "if r0 == 0 goto i_loop_end_%=;" |
1097 | "check_one_r6_%=:" |
1098 | "if r6 != 1 goto check_zero_r6_%=;" |
1099 | "r6 = 0;" |
1100 | "r7 = 1;" |
1101 | "goto i_loop_%=;" |
1102 | "check_zero_r6_%=:" |
1103 | "if r6 != 0 goto i_loop_%=;" |
1104 | "r6 = 1;" |
1105 | "call %[bpf_get_prandom_u32];" |
1106 | "if r0 != 42 goto check_one_r7_%=;" |
1107 | "goto i_loop_%=;" |
1108 | "check_one_r7_%=:" |
1109 | "if r7 != 1 goto i_loop_%=;" |
1110 | "r0 = r10;" |
1111 | "r0 += r8;" |
1112 | "r1 = 7;" |
1113 | "*(u64 *)(r0 + 0) = r1;" |
1114 | "r1 = r10;" |
1115 | "r1 += -8;" |
1116 | "call %[bpf_iter_num_destroy];" |
1117 | "r1 = r10;" |
1118 | "r1 += -16;" |
1119 | "call %[bpf_iter_num_destroy];" |
1120 | "r0 = 0;" |
1121 | "exit;" |
1122 | "i_loop_end_%=:" |
1123 | "r1 = r10;" |
1124 | "r1 += -8;" |
1125 | "call %[bpf_iter_num_destroy];" |
1126 | |
1127 | /* second inner loop */ |
1128 | "r1 = r10;" |
1129 | "r1 += -8;" |
1130 | "r2 = 0;" |
1131 | "r3 = 10;" |
1132 | "call %[bpf_iter_num_new];" |
1133 | "r6 = 0;" |
1134 | "r7 = 0;" |
1135 | "i2_loop_%=:" |
1136 | "r1 = r10;" |
1137 | "r1 += -8;" |
1138 | "call %[bpf_iter_num_next];" |
1139 | "if r0 == 0 goto i2_loop_end_%=;" |
1140 | "check2_one_r6_%=:" |
1141 | "if r6 != 1 goto check2_zero_r6_%=;" |
1142 | "r6 = 0;" |
1143 | "r7 = 1;" |
1144 | "goto i2_loop_%=;" |
1145 | "check2_zero_r6_%=:" |
1146 | "if r6 != 0 goto i2_loop_%=;" |
1147 | "r6 = 1;" |
1148 | "call %[bpf_get_prandom_u32];" |
1149 | "if r0 != 42 goto check2_one_r7_%=;" |
1150 | "goto i2_loop_%=;" |
1151 | "check2_one_r7_%=:" |
1152 | "if r7 != 1 goto i2_loop_%=;" |
1153 | "r6 = 0;" |
1154 | "r8 = -25;" |
1155 | "goto i2_loop_%=;" |
1156 | "i2_loop_end_%=:" |
1157 | "r1 = r10;" |
1158 | "r1 += -8;" |
1159 | "call %[bpf_iter_num_destroy];" |
1160 | |
1161 | "r6 = 0;" |
1162 | "r7 = 0;" |
1163 | "goto j_loop_%=;" |
1164 | "j_loop_end_%=:" |
1165 | "r1 = r10;" |
1166 | "r1 += -16;" |
1167 | "call %[bpf_iter_num_destroy];" |
1168 | "r0 = 0;" |
1169 | "exit;" |
1170 | : |
1171 | : __imm(bpf_get_prandom_u32), |
1172 | __imm(bpf_iter_num_new), |
1173 | __imm(bpf_iter_num_next), |
1174 | __imm(bpf_iter_num_destroy) |
1175 | : __clobber_all |
1176 | ); |
1177 | } |
1178 | |
1179 | SEC("?raw_tp" ) |
1180 | __success |
1181 | __naked int triple_continue(void) |
1182 | { |
1183 | /* This is equivalent to C program below. |
1184 | * High branching factor of the loop body turned out to be |
1185 | * problematic for one of the iterator convergence tracking |
1186 | * algorithms explored. |
1187 | * |
1188 | * r6 = bpf_get_prandom_u32() |
1189 | * bpf_iter_num_new(&fp[-8], 0, 10) |
1190 | * while (bpf_iter_num_next(&fp[-8])) { |
1191 | * if (bpf_get_prandom_u32() != 42) |
1192 | * continue; |
1193 | * if (bpf_get_prandom_u32() != 42) |
1194 | * continue; |
1195 | * if (bpf_get_prandom_u32() != 42) |
1196 | * continue; |
1197 | * r0 += 0; |
1198 | * } |
1199 | * bpf_iter_num_destroy(&fp[-8]) |
1200 | * return 0 |
1201 | */ |
1202 | asm volatile ( |
1203 | "r1 = r10;" |
1204 | "r1 += -8;" |
1205 | "r2 = 0;" |
1206 | "r3 = 10;" |
1207 | "call %[bpf_iter_num_new];" |
1208 | "loop_%=:" |
1209 | "r1 = r10;" |
1210 | "r1 += -8;" |
1211 | "call %[bpf_iter_num_next];" |
1212 | "if r0 == 0 goto loop_end_%=;" |
1213 | "call %[bpf_get_prandom_u32];" |
1214 | "if r0 != 42 goto loop_%=;" |
1215 | "call %[bpf_get_prandom_u32];" |
1216 | "if r0 != 42 goto loop_%=;" |
1217 | "call %[bpf_get_prandom_u32];" |
1218 | "if r0 != 42 goto loop_%=;" |
1219 | "r0 += 0;" |
1220 | "goto loop_%=;" |
1221 | "loop_end_%=:" |
1222 | "r1 = r10;" |
1223 | "r1 += -8;" |
1224 | "call %[bpf_iter_num_destroy];" |
1225 | "r0 = 0;" |
1226 | "exit;" |
1227 | : |
1228 | : __imm(bpf_get_prandom_u32), |
1229 | __imm(bpf_iter_num_new), |
1230 | __imm(bpf_iter_num_next), |
1231 | __imm(bpf_iter_num_destroy) |
1232 | : __clobber_all |
1233 | ); |
1234 | } |
1235 | |
1236 | SEC("?raw_tp" ) |
1237 | __success |
1238 | __naked int widen_spill(void) |
1239 | { |
1240 | /* This is equivalent to C program below. |
1241 | * The counter is stored in fp[-16], if this counter is not widened |
1242 | * verifier states representing loop iterations would never converge. |
1243 | * |
1244 | * fp[-16] = 0 |
1245 | * bpf_iter_num_new(&fp[-8], 0, 10) |
1246 | * while (bpf_iter_num_next(&fp[-8])) { |
1247 | * r0 = fp[-16]; |
1248 | * r0 += 1; |
1249 | * fp[-16] = r0; |
1250 | * } |
1251 | * bpf_iter_num_destroy(&fp[-8]) |
1252 | * return 0 |
1253 | */ |
1254 | asm volatile ( |
1255 | "r0 = 0;" |
1256 | "*(u64 *)(r10 - 16) = r0;" |
1257 | "r1 = r10;" |
1258 | "r1 += -8;" |
1259 | "r2 = 0;" |
1260 | "r3 = 10;" |
1261 | "call %[bpf_iter_num_new];" |
1262 | "loop_%=:" |
1263 | "r1 = r10;" |
1264 | "r1 += -8;" |
1265 | "call %[bpf_iter_num_next];" |
1266 | "if r0 == 0 goto loop_end_%=;" |
1267 | "r0 = *(u64 *)(r10 - 16);" |
1268 | "r0 += 1;" |
1269 | "*(u64 *)(r10 - 16) = r0;" |
1270 | "goto loop_%=;" |
1271 | "loop_end_%=:" |
1272 | "r1 = r10;" |
1273 | "r1 += -8;" |
1274 | "call %[bpf_iter_num_destroy];" |
1275 | "r0 = 0;" |
1276 | "exit;" |
1277 | : |
1278 | : __imm(bpf_iter_num_new), |
1279 | __imm(bpf_iter_num_next), |
1280 | __imm(bpf_iter_num_destroy) |
1281 | : __clobber_all |
1282 | ); |
1283 | } |
1284 | |
1285 | SEC("raw_tp" ) |
1286 | __success |
1287 | __naked int checkpoint_states_deletion(void) |
1288 | { |
1289 | /* This is equivalent to C program below. |
1290 | * |
1291 | * int *a, *b, *c, *d, *e, *f; |
1292 | * int i, sum = 0; |
1293 | * bpf_for(i, 0, 10) { |
1294 | * a = bpf_map_lookup_elem(&amap, &i); |
1295 | * b = bpf_map_lookup_elem(&amap, &i); |
1296 | * c = bpf_map_lookup_elem(&amap, &i); |
1297 | * d = bpf_map_lookup_elem(&amap, &i); |
1298 | * e = bpf_map_lookup_elem(&amap, &i); |
1299 | * f = bpf_map_lookup_elem(&amap, &i); |
1300 | * if (a) sum += 1; |
1301 | * if (b) sum += 1; |
1302 | * if (c) sum += 1; |
1303 | * if (d) sum += 1; |
1304 | * if (e) sum += 1; |
1305 | * if (f) sum += 1; |
1306 | * } |
1307 | * return 0; |
1308 | * |
1309 | * The body of the loop spawns multiple simulation paths |
1310 | * with different combination of NULL/non-NULL information for a/b/c/d/e/f. |
1311 | * Each combination is unique from states_equal() point of view. |
1312 | * Explored states checkpoint is created after each iterator next call. |
1313 | * Iterator convergence logic expects that eventually current state |
1314 | * would get equal to one of the explored states and thus loop |
1315 | * exploration would be finished (at-least for a specific path). |
1316 | * Verifier evicts explored states with high miss to hit ratio |
1317 | * to to avoid comparing current state with too many explored |
1318 | * states per instruction. |
1319 | * This test is designed to "stress test" eviction policy defined using formula: |
1320 | * |
1321 | * sl->miss_cnt > sl->hit_cnt * N + N // if true sl->state is evicted |
1322 | * |
1323 | * Currently N is set to 64, which allows for 6 variables in this test. |
1324 | */ |
1325 | asm volatile ( |
1326 | "r6 = 0;" /* a */ |
1327 | "r7 = 0;" /* b */ |
1328 | "r8 = 0;" /* c */ |
1329 | "*(u64 *)(r10 - 24) = r6;" /* d */ |
1330 | "*(u64 *)(r10 - 32) = r6;" /* e */ |
1331 | "*(u64 *)(r10 - 40) = r6;" /* f */ |
1332 | "r9 = 0;" /* sum */ |
1333 | "r1 = r10;" |
1334 | "r1 += -8;" |
1335 | "r2 = 0;" |
1336 | "r3 = 10;" |
1337 | "call %[bpf_iter_num_new];" |
1338 | "loop_%=:" |
1339 | "r1 = r10;" |
1340 | "r1 += -8;" |
1341 | "call %[bpf_iter_num_next];" |
1342 | "if r0 == 0 goto loop_end_%=;" |
1343 | |
1344 | "*(u64 *)(r10 - 16) = r0;" |
1345 | |
1346 | "r1 = %[amap] ll;" |
1347 | "r2 = r10;" |
1348 | "r2 += -16;" |
1349 | "call %[bpf_map_lookup_elem];" |
1350 | "r6 = r0;" |
1351 | |
1352 | "r1 = %[amap] ll;" |
1353 | "r2 = r10;" |
1354 | "r2 += -16;" |
1355 | "call %[bpf_map_lookup_elem];" |
1356 | "r7 = r0;" |
1357 | |
1358 | "r1 = %[amap] ll;" |
1359 | "r2 = r10;" |
1360 | "r2 += -16;" |
1361 | "call %[bpf_map_lookup_elem];" |
1362 | "r8 = r0;" |
1363 | |
1364 | "r1 = %[amap] ll;" |
1365 | "r2 = r10;" |
1366 | "r2 += -16;" |
1367 | "call %[bpf_map_lookup_elem];" |
1368 | "*(u64 *)(r10 - 24) = r0;" |
1369 | |
1370 | "r1 = %[amap] ll;" |
1371 | "r2 = r10;" |
1372 | "r2 += -16;" |
1373 | "call %[bpf_map_lookup_elem];" |
1374 | "*(u64 *)(r10 - 32) = r0;" |
1375 | |
1376 | "r1 = %[amap] ll;" |
1377 | "r2 = r10;" |
1378 | "r2 += -16;" |
1379 | "call %[bpf_map_lookup_elem];" |
1380 | "*(u64 *)(r10 - 40) = r0;" |
1381 | |
1382 | "if r6 == 0 goto +1;" |
1383 | "r9 += 1;" |
1384 | "if r7 == 0 goto +1;" |
1385 | "r9 += 1;" |
1386 | "if r8 == 0 goto +1;" |
1387 | "r9 += 1;" |
1388 | "r0 = *(u64 *)(r10 - 24);" |
1389 | "if r0 == 0 goto +1;" |
1390 | "r9 += 1;" |
1391 | "r0 = *(u64 *)(r10 - 32);" |
1392 | "if r0 == 0 goto +1;" |
1393 | "r9 += 1;" |
1394 | "r0 = *(u64 *)(r10 - 40);" |
1395 | "if r0 == 0 goto +1;" |
1396 | "r9 += 1;" |
1397 | |
1398 | "goto loop_%=;" |
1399 | "loop_end_%=:" |
1400 | "r1 = r10;" |
1401 | "r1 += -8;" |
1402 | "call %[bpf_iter_num_destroy];" |
1403 | "r0 = 0;" |
1404 | "exit;" |
1405 | : |
1406 | : __imm(bpf_map_lookup_elem), |
1407 | __imm(bpf_iter_num_new), |
1408 | __imm(bpf_iter_num_next), |
1409 | __imm(bpf_iter_num_destroy), |
1410 | __imm_addr(amap) |
1411 | : __clobber_all |
1412 | ); |
1413 | } |
1414 | |
1415 | struct { |
1416 | int data[32]; |
1417 | int n; |
1418 | } loop_data; |
1419 | |
1420 | SEC("raw_tp" ) |
1421 | __success |
1422 | int iter_arr_with_actual_elem_count(const void *ctx) |
1423 | { |
1424 | int i, n = loop_data.n, sum = 0; |
1425 | |
1426 | if (n > ARRAY_SIZE(loop_data.data)) |
1427 | return 0; |
1428 | |
1429 | bpf_for(i, 0, n) { |
1430 | /* no rechecking of i against ARRAY_SIZE(loop_data.n) */ |
1431 | sum += loop_data.data[i]; |
1432 | } |
1433 | |
1434 | return sum; |
1435 | } |
1436 | |
1437 | char _license[] SEC("license" ) = "GPL" ; |
1438 | |