Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add tests for add_const

Improve arena based tests and add several C and asm tests
with specific pattern.
These tests would have failed without add_const verifier support.

Also add several loop_inside_iter*() tests that are not related to add_const,
but nice to have.

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20240613013815.953-5-alexei.starovoitov@gmail.com

authored by

Alexei Starovoitov and committed by
Daniel Borkmann
dedf56d7 6870bdb3

+251 -5
+15 -5
tools/testing/selftests/bpf/progs/arena_htab.c
··· 19 19 bool skip = false; 20 20 21 21 int zero = 0; 22 + char __arena arr1[100000]; 23 + char arr2[1000]; 22 24 23 25 SEC("syscall") 24 26 int arena_htab_llvm(void *ctx) 25 27 { 26 28 #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) || defined(BPF_ARENA_FORCE_ASM) 27 29 struct htab __arena *htab; 30 + char __arena *arr = arr1; 28 31 __u64 i; 29 32 30 33 htab = bpf_alloc(sizeof(*htab)); 31 34 cast_kern(htab); 32 35 htab_init(htab); 33 36 34 - /* first run. No old elems in the table */ 35 - for (i = zero; i < 1000; i++) 36 - htab_update_elem(htab, i, i); 37 + cast_kern(arr); 37 38 38 - /* should replace all elems with new ones */ 39 - for (i = zero; i < 1000; i++) 39 + /* first run. No old elems in the table */ 40 + for (i = zero; i < 100000 && can_loop; i++) { 40 41 htab_update_elem(htab, i, i); 42 + arr[i] = i; 43 + } 44 + 45 + /* should replace some elems with new ones */ 46 + for (i = zero; i < 1000 && can_loop; i++) { 47 + htab_update_elem(htab, i, i); 48 + /* Access mem to make the verifier use bounded loop logic */ 49 + arr2[i] = i; 50 + } 41 51 cast_user(htab); 42 52 htab_for_user = htab; 43 53 #else
+236
tools/testing/selftests/bpf/progs/verifier_iterating_callbacks.c
··· 405 405 return cnt1 > 1 && cnt2 > 1 ? 1 : 0; 406 406 } 407 407 408 + #define ARR2_SZ 1000 409 + SEC(".data.arr2") 410 + char arr2[ARR2_SZ]; 411 + 412 + SEC("socket") 413 + __success __flag(BPF_F_TEST_STATE_FREQ) 414 + int loop_inside_iter(const void *ctx) 415 + { 416 + struct bpf_iter_num it; 417 + int *v, sum = 0; 418 + __u64 i = 0; 419 + 420 + bpf_iter_num_new(&it, 0, ARR2_SZ); 421 + while ((v = bpf_iter_num_next(&it))) { 422 + if (i < ARR2_SZ) 423 + sum += arr2[i++]; 424 + } 425 + bpf_iter_num_destroy(&it); 426 + return sum; 427 + } 428 + 429 + SEC("socket") 430 + __success __flag(BPF_F_TEST_STATE_FREQ) 431 + int loop_inside_iter_signed(const void *ctx) 432 + { 433 + struct bpf_iter_num it; 434 + int *v, sum = 0; 435 + long i = 0; 436 + 437 + bpf_iter_num_new(&it, 0, ARR2_SZ); 438 + while ((v = bpf_iter_num_next(&it))) { 439 + if (i < ARR2_SZ && i >= 0) 440 + sum += arr2[i++]; 441 + } 442 + bpf_iter_num_destroy(&it); 443 + return sum; 444 + } 445 + 446 + volatile const int limit = ARR2_SZ; 447 + 448 + SEC("socket") 449 + __success __flag(BPF_F_TEST_STATE_FREQ) 450 + int loop_inside_iter_volatile_limit(const void *ctx) 451 + { 452 + struct bpf_iter_num it; 453 + int *v, sum = 0; 454 + __u64 i = 0; 455 + 456 + bpf_iter_num_new(&it, 0, ARR2_SZ); 457 + while ((v = bpf_iter_num_next(&it))) { 458 + if (i < limit) 459 + sum += arr2[i++]; 460 + } 461 + bpf_iter_num_destroy(&it); 462 + return sum; 463 + } 464 + 465 + #define ARR_LONG_SZ 1000 466 + 467 + SEC(".data.arr_long") 468 + long arr_long[ARR_LONG_SZ]; 469 + 470 + SEC("socket") 471 + __success 472 + int test1(const void *ctx) 473 + { 474 + long i; 475 + 476 + for (i = 0; i < ARR_LONG_SZ && can_loop; i++) 477 + arr_long[i] = i; 478 + return 0; 479 + } 480 + 481 + SEC("socket") 482 + __success 483 + int test2(const void *ctx) 484 + { 485 + __u64 i; 486 + 487 + for (i = zero; i < ARR_LONG_SZ && can_loop; i++) { 488 + barrier_var(i); 489 + arr_long[i] = i; 490 + } 491 + return 0; 492 + } 493 + 494 + SEC(".data.arr_foo") 495 + struct { 496 + int a; 497 + int b; 498 + } arr_foo[ARR_LONG_SZ]; 499 + 500 + SEC("socket") 501 + __success 502 + int test3(const void *ctx) 503 + { 504 + __u64 i; 505 + 506 + for (i = zero; i < ARR_LONG_SZ && can_loop; i++) { 507 + barrier_var(i); 508 + arr_foo[i].a = i; 509 + arr_foo[i].b = i; 510 + } 511 + return 0; 512 + } 513 + 514 + SEC("socket") 515 + __success 516 + int test4(const void *ctx) 517 + { 518 + long i; 519 + 520 + for (i = zero + ARR_LONG_SZ - 1; i < ARR_LONG_SZ && i >= 0 && can_loop; i--) { 521 + barrier_var(i); 522 + arr_foo[i].a = i; 523 + arr_foo[i].b = i; 524 + } 525 + return 0; 526 + } 527 + 528 + char buf[10] SEC(".data.buf"); 529 + 530 + SEC("socket") 531 + __description("check add const") 532 + __success 533 + __naked void check_add_const(void) 534 + { 535 + /* typical LLVM generated loop with may_goto */ 536 + asm volatile (" \ 537 + call %[bpf_ktime_get_ns]; \ 538 + if r0 > 9 goto l1_%=; \ 539 + l0_%=: r1 = %[buf]; \ 540 + r2 = r0; \ 541 + r1 += r2; \ 542 + r3 = *(u8 *)(r1 +0); \ 543 + .byte 0xe5; /* may_goto */ \ 544 + .byte 0; /* regs */ \ 545 + .short 4; /* off of l1_%=: */ \ 546 + .long 0; /* imm */ \ 547 + r0 = r2; \ 548 + r0 += 1; \ 549 + if r2 < 9 goto l0_%=; \ 550 + exit; \ 551 + l1_%=: r0 = 0; \ 552 + exit; \ 553 + " : 554 + : __imm(bpf_ktime_get_ns), 555 + __imm_ptr(buf) 556 + : __clobber_common); 557 + } 558 + 559 + SEC("socket") 560 + __failure 561 + __msg("*(u8 *)(r7 +0) = r0") 562 + __msg("invalid access to map value, value_size=10 off=10 size=1") 563 + __naked void check_add_const_3regs(void) 564 + { 565 + asm volatile ( 566 + "r6 = %[buf];" 567 + "r7 = %[buf];" 568 + "call %[bpf_ktime_get_ns];" 569 + "r1 = r0;" /* link r0.id == r1.id == r2.id */ 570 + "r2 = r0;" 571 + "r1 += 1;" /* r1 == r0+1 */ 572 + "r2 += 2;" /* r2 == r0+2 */ 573 + "if r0 > 8 goto 1f;" /* r0 range [0, 8] */ 574 + "r6 += r1;" /* r1 range [1, 9] */ 575 + "r7 += r2;" /* r2 range [2, 10] */ 576 + "*(u8 *)(r6 +0) = r0;" /* safe, within bounds */ 577 + "*(u8 *)(r7 +0) = r0;" /* unsafe, out of bounds */ 578 + "1: exit;" 579 + : 580 + : __imm(bpf_ktime_get_ns), 581 + __imm_ptr(buf) 582 + : __clobber_common); 583 + } 584 + 585 + SEC("socket") 586 + __failure 587 + __msg("*(u8 *)(r8 -1) = r0") 588 + __msg("invalid access to map value, value_size=10 off=10 size=1") 589 + __naked void check_add_const_3regs_2if(void) 590 + { 591 + asm volatile ( 592 + "r6 = %[buf];" 593 + "r7 = %[buf];" 594 + "r8 = %[buf];" 595 + "call %[bpf_ktime_get_ns];" 596 + "if r0 < 2 goto 1f;" 597 + "r1 = r0;" /* link r0.id == r1.id == r2.id */ 598 + "r2 = r0;" 599 + "r1 += 1;" /* r1 == r0+1 */ 600 + "r2 += 2;" /* r2 == r0+2 */ 601 + "if r2 > 11 goto 1f;" /* r2 range [0, 11] -> r0 range [-2, 9]; r1 range [-1, 10] */ 602 + "if r0 s< 0 goto 1f;" /* r0 range [0, 9] -> r1 range [1, 10]; r2 range [2, 11]; */ 603 + "r6 += r0;" /* r0 range [0, 9] */ 604 + "r7 += r1;" /* r1 range [1, 10] */ 605 + "r8 += r2;" /* r2 range [2, 11] */ 606 + "*(u8 *)(r6 +0) = r0;" /* safe, within bounds */ 607 + "*(u8 *)(r7 -1) = r0;" /* safe */ 608 + "*(u8 *)(r8 -1) = r0;" /* unsafe */ 609 + "1: exit;" 610 + : 611 + : __imm(bpf_ktime_get_ns), 612 + __imm_ptr(buf) 613 + : __clobber_common); 614 + } 615 + 616 + SEC("socket") 617 + __failure 618 + __flag(BPF_F_TEST_STATE_FREQ) 619 + __naked void check_add_const_regsafe_off(void) 620 + { 621 + asm volatile ( 622 + "r8 = %[buf];" 623 + "call %[bpf_ktime_get_ns];" 624 + "r6 = r0;" 625 + "call %[bpf_ktime_get_ns];" 626 + "r7 = r0;" 627 + "call %[bpf_ktime_get_ns];" 628 + "r1 = r0;" /* same ids for r1 and r0 */ 629 + "if r6 > r7 goto 1f;" /* this jump can't be predicted */ 630 + "r1 += 1;" /* r1.off == +1 */ 631 + "goto 2f;" 632 + "1: r1 += 100;" /* r1.off == +100 */ 633 + "goto +0;" /* verify r1.off in regsafe() after this insn */ 634 + "2: if r0 > 8 goto 3f;" /* r0 range [0,8], r1 range either [1,9] or [100,108]*/ 635 + "r8 += r1;" 636 + "*(u8 *)(r8 +0) = r0;" /* potentially unsafe, buf size is 10 */ 637 + "3: exit;" 638 + : 639 + : __imm(bpf_ktime_get_ns), 640 + __imm_ptr(buf) 641 + : __clobber_common); 642 + } 643 + 408 644 char _license[] SEC("license") = "GPL";