1 // SPDX-License-Identifier: GPL-2.0 2 #include "bpf_misc.h" 3 #include "bpf_experimental.h" 4 5 struct { 6 __uint(type, BPF_MAP_TYPE_ARRAY); 7 __uint(max_entries, 8); 8 __type(key, __u32); 9 __type(value, __u64); 10 } map SEC(".maps"); 11 12 struct { 13 __uint(type, BPF_MAP_TYPE_USER_RINGBUF); 14 __uint(max_entries, 8); 15 } ringbuf SEC(".maps"); 16 17 struct vm_area_struct; 18 struct bpf_map; 19 20 struct buf_context { 21 char *buf; 22 }; 23 24 struct num_context { 25 __u64 i; 26 __u64 j; 27 }; 28 29 __u8 choice_arr[2] = { 0, 1 }; 30 31 static int unsafe_on_2nd_iter_cb(__u32 idx, struct buf_context *ctx) 32 { 33 if (idx == 0) { 34 ctx->buf = (char *)(0xDEAD); 35 return 0; 36 } 37 38 if (bpf_probe_read_user(ctx->buf, 8, (void *)(0xBADC0FFEE))) 39 return 1; 40 41 return 0; 42 } 43 44 SEC("?raw_tp") 45 __failure __msg("R1 type=scalar expected=fp") 46 int unsafe_on_2nd_iter(void *unused) 47 { 48 char buf[4]; 49 struct buf_context loop_ctx = { .buf = buf }; 50 51 bpf_loop(100, unsafe_on_2nd_iter_cb, &loop_ctx, 0); 52 return 0; 53 } 54 55 static int unsafe_on_zero_iter_cb(__u32 idx, struct num_context *ctx) 56 { 57 ctx->i = 0; 58 return 0; 59 } 60 61 SEC("?raw_tp") 62 __failure __msg("invalid access to map value, value_size=2 off=32 size=1") 63 int unsafe_on_zero_iter(void *unused) 64 { 65 struct num_context loop_ctx = { .i = 32 }; 66 67 bpf_loop(100, unsafe_on_zero_iter_cb, &loop_ctx, 0); 68 return choice_arr[loop_ctx.i]; 69 } 70 71 static int widening_cb(__u32 idx, struct num_context *ctx) 72 { 73 ++ctx->i; 74 return 0; 75 } 76 77 SEC("?raw_tp") 78 __success 79 int widening(void *unused) 80 { 81 struct num_context loop_ctx = { .i = 0, .j = 1 }; 82 83 bpf_loop(100, widening_cb, &loop_ctx, 0); 84 /* loop_ctx.j is not changed during callback iteration, 85 * verifier should not apply widening to it. 86 */ 87 return choice_arr[loop_ctx.j]; 88 } 89 90 static int loop_detection_cb(__u32 idx, struct num_context *ctx) 91 { 92 for (;;) {} 93 return 0; 94 } 95 96 SEC("?raw_tp") 97 __failure __msg("infinite loop detected") 98 int loop_detection(void *unused) 99 { 100 struct num_context loop_ctx = { .i = 0 }; 101 102 bpf_loop(100, loop_detection_cb, &loop_ctx, 0); 103 return 0; 104 } 105 106 static __always_inline __u64 oob_state_machine(struct num_context *ctx) 107 { 108 switch (ctx->i) { 109 case 0: 110 ctx->i = 1; 111 break; 112 case 1: 113 ctx->i = 32; 114 break; 115 } 116 return 0; 117 } 118 119 static __u64 for_each_map_elem_cb(struct bpf_map *map, __u32 *key, __u64 *val, void *data) 120 { 121 return oob_state_machine(data); 122 } 123 124 SEC("?raw_tp") 125 __failure __msg("invalid access to map value, value_size=2 off=32 size=1") 126 int unsafe_for_each_map_elem(void *unused) 127 { 128 struct num_context loop_ctx = { .i = 0 }; 129 130 bpf_for_each_map_elem(&map, for_each_map_elem_cb, &loop_ctx, 0); 131 return choice_arr[loop_ctx.i]; 132 } 133 134 static __u64 ringbuf_drain_cb(struct bpf_dynptr *dynptr, void *data) 135 { 136 return oob_state_machine(data); 137 } 138 139 SEC("?raw_tp") 140 __failure __msg("invalid access to map value, value_size=2 off=32 size=1") 141 int unsafe_ringbuf_drain(void *unused) 142 { 143 struct num_context loop_ctx = { .i = 0 }; 144 145 bpf_user_ringbuf_drain(&ringbuf, ringbuf_drain_cb, &loop_ctx, 0); 146 return choice_arr[loop_ctx.i]; 147 } 148 149 static __u64 find_vma_cb(struct task_struct *task, struct vm_area_struct *vma, void *data) 150 { 151 return oob_state_machine(data); 152 } 153 154 SEC("?raw_tp") 155 __failure __msg("invalid access to map value, value_size=2 off=32 size=1") 156 int unsafe_find_vma(void *unused) 157 { 158 struct task_struct *task = bpf_get_current_task_btf(); 159 struct num_context loop_ctx = { .i = 0 }; 160 161 bpf_find_vma(task, 0, find_vma_cb, &loop_ctx, 0); 162 return choice_arr[loop_ctx.i]; 163 } 164 165 static int iter_limit_cb(__u32 idx, struct num_context *ctx) 166 { 167 ctx->i++; 168 return 0; 169 } 170 171 SEC("?raw_tp") 172 __failure __msg("R1 type=ctx expected=scalar") 173 __naked void bpf_loop_reject_pointer(void) 174 { 175 asm volatile ( 176 "r2 = %[iter_limit_cb];" 177 "r3 = 0;" 178 "r4 = 0;" 179 "call %[bpf_loop];" 180 "exit;" 181 : 182 : __imm_ptr(iter_limit_cb), 183 __imm(bpf_loop) 184 : __clobber_common 185 ); 186 } 187 188 SEC("?raw_tp") 189 __success 190 int bpf_loop_iter_limit_ok(void *unused) 191 { 192 struct num_context ctx = { .i = 0 }; 193 194 bpf_loop(1, iter_limit_cb, &ctx, 0); 195 return choice_arr[ctx.i]; 196 } 197 198 SEC("?raw_tp") 199 __failure __msg("invalid access to map value, value_size=2 off=2 size=1") 200 int bpf_loop_iter_limit_overflow(void *unused) 201 { 202 struct num_context ctx = { .i = 0 }; 203 204 bpf_loop(2, iter_limit_cb, &ctx, 0); 205 return choice_arr[ctx.i]; 206 } 207 208 static int iter_limit_level2a_cb(__u32 idx, struct num_context *ctx) 209 { 210 ctx->i += 100; 211 return 0; 212 } 213 214 static int iter_limit_level2b_cb(__u32 idx, struct num_context *ctx) 215 { 216 ctx->i += 10; 217 return 0; 218 } 219 220 static int iter_limit_level1_cb(__u32 idx, struct num_context *ctx) 221 { 222 ctx->i += 1; 223 bpf_loop(1, iter_limit_level2a_cb, ctx, 0); 224 bpf_loop(1, iter_limit_level2b_cb, ctx, 0); 225 return 0; 226 } 227 228 /* Check that path visiting every callback function once had been 229 * reached by verifier. Variables 'ctx{1,2}i' below serve as flags, 230 * with each decimal digit corresponding to a callback visit marker. 231 */ 232 SEC("socket") 233 __success __retval(111111) 234 int bpf_loop_iter_limit_nested(void *unused) 235 { 236 struct num_context ctx1 = { .i = 0 }; 237 struct num_context ctx2 = { .i = 0 }; 238 __u64 a, b, c; 239 240 bpf_loop(1, iter_limit_level1_cb, &ctx1, 0); 241 bpf_loop(1, iter_limit_level1_cb, &ctx2, 0); 242 a = ctx1.i; 243 b = ctx2.i; 244 /* Force 'ctx1.i' and 'ctx2.i' precise. */ 245 c = choice_arr[(a + b) % 2]; 246 /* This makes 'c' zero, but neither clang nor verifier know it. */ 247 c /= 10; 248 /* Make sure that verifier does not visit 'impossible' states: 249 * enumerate all possible callback visit masks. 250 */ 251 if (a != 0 && a != 1 && a != 11 && a != 101 && a != 111 && 252 b != 0 && b != 1 && b != 11 && b != 101 && b != 111) 253 asm volatile ("r0 /= 0;" ::: "r0"); 254 return 1000 * a + b + c; 255 } 256 257 struct iter_limit_bug_ctx { 258 __u64 a; 259 __u64 b; 260 __u64 c; 261 }; 262 263 static __naked void iter_limit_bug_cb(void) 264 { 265 /* This is the same as C code below, but written 266 * in assembly to control which branches are fall-through. 267 * 268 * switch (bpf_get_prandom_u32()) { 269 * case 1: ctx->a = 42; break; 270 * case 2: ctx->b = 42; break; 271 * default: ctx->c = 42; break; 272 * } 273 */ 274 asm volatile ( 275 "r9 = r2;" 276 "call %[bpf_get_prandom_u32];" 277 "r1 = r0;" 278 "r2 = 42;" 279 "r0 = 0;" 280 "if r1 == 0x1 goto 1f;" 281 "if r1 == 0x2 goto 2f;" 282 "*(u64 *)(r9 + 16) = r2;" 283 "exit;" 284 "1: *(u64 *)(r9 + 0) = r2;" 285 "exit;" 286 "2: *(u64 *)(r9 + 8) = r2;" 287 "exit;" 288 : 289 : __imm(bpf_get_prandom_u32) 290 : __clobber_all 291 ); 292 } 293 294 int tmp_var; 295 SEC("socket") 296 __failure __msg("infinite loop detected at insn 2") 297 __naked void jgt_imm64_and_may_goto(void) 298 { 299 asm volatile (" \ 300 r0 = %[tmp_var] ll; \ 301 l0_%=: .byte 0xe5; /* may_goto */ \ 302 .byte 0; /* regs */ \ 303 .short -3; /* off -3 */ \ 304 .long 0; /* imm */ \ 305 if r0 > 10 goto l0_%=; \ 306 r0 = 0; \ 307 exit; \ 308 " :: __imm_addr(tmp_var) 309 : __clobber_all); 310 } 311 312 SEC("socket") 313 __failure __msg("infinite loop detected at insn 1") 314 __naked void may_goto_self(void) 315 { 316 asm volatile (" \ 317 r0 = *(u32 *)(r10 - 4); \ 318 l0_%=: .byte 0xe5; /* may_goto */ \ 319 .byte 0; /* regs */ \ 320 .short -1; /* off -1 */ \ 321 .long 0; /* imm */ \ 322 if r0 > 10 goto l0_%=; \ 323 r0 = 0; \ 324 exit; \ 325 " ::: __clobber_all); 326 } 327 328 SEC("socket") 329 __success __retval(0) 330 __naked void may_goto_neg_off(void) 331 { 332 asm volatile (" \ 333 r0 = *(u32 *)(r10 - 4); \ 334 goto l0_%=; \ 335 goto l1_%=; \ 336 l0_%=: .byte 0xe5; /* may_goto */ \ 337 .byte 0; /* regs */ \ 338 .short -2; /* off -2 */ \ 339 .long 0; /* imm */ \ 340 if r0 > 10 goto l0_%=; \ 341 l1_%=: r0 = 0; \ 342 exit; \ 343 " ::: __clobber_all); 344 } 345 346 SEC("tc") 347 __failure 348 __flag(BPF_F_TEST_STATE_FREQ) 349 int iter_limit_bug(struct __sk_buff *skb) 350 { 351 struct iter_limit_bug_ctx ctx = { 7, 7, 7 }; 352 353 bpf_loop(2, iter_limit_bug_cb, &ctx, 0); 354 355 /* This is the same as C code below, 356 * written in assembly to guarantee checks order. 357 * 358 * if (ctx.a == 42 && ctx.b == 42 && ctx.c == 7) 359 * asm volatile("r1 /= 0;":::"r1"); 360 */ 361 asm volatile ( 362 "r1 = *(u64 *)%[ctx_a];" 363 "if r1 != 42 goto 1f;" 364 "r1 = *(u64 *)%[ctx_b];" 365 "if r1 != 42 goto 1f;" 366 "r1 = *(u64 *)%[ctx_c];" 367 "if r1 != 7 goto 1f;" 368 "r1 /= 0;" 369 "1:" 370 : 371 : [ctx_a]"m"(ctx.a), 372 [ctx_b]"m"(ctx.b), 373 [ctx_c]"m"(ctx.c) 374 : "r1" 375 ); 376 return 0; 377 } 378 379 SEC("socket") 380 __success __retval(0) 381 __naked void ja_and_may_goto(void) 382 { 383 asm volatile (" \ 384 l0_%=: .byte 0xe5; /* may_goto */ \ 385 .byte 0; /* regs */ \ 386 .short 1; /* off 1 */ \ 387 .long 0; /* imm */ \ 388 goto l0_%=; \ 389 r0 = 0; \ 390 exit; \ 391 " ::: __clobber_common); 392 } 393 394 SEC("socket") 395 __success __retval(0) 396 __naked void ja_and_may_goto2(void) 397 { 398 asm volatile (" \ 399 l0_%=: r0 = 0; \ 400 .byte 0xe5; /* may_goto */ \ 401 .byte 0; /* regs */ \ 402 .short 1; /* off 1 */ \ 403 .long 0; /* imm */ \ 404 goto l0_%=; \ 405 r0 = 0; \ 406 exit; \ 407 " ::: __clobber_common); 408 } 409 410 SEC("socket") 411 __success __retval(0) 412 __naked void jlt_and_may_goto(void) 413 { 414 asm volatile (" \ 415 l0_%=: call %[bpf_jiffies64]; \ 416 .byte 0xe5; /* may_goto */ \ 417 .byte 0; /* regs */ \ 418 .short 1; /* off 1 */ \ 419 .long 0; /* imm */ \ 420 if r0 < 10 goto l0_%=; \ 421 r0 = 0; \ 422 exit; \ 423 " :: __imm(bpf_jiffies64) 424 : __clobber_all); 425 } 426 427 #ifdef CAN_USE_GOTOL 428 SEC("socket") 429 __success __retval(0) 430 __naked void gotol_and_may_goto(void) 431 { 432 asm volatile (" \ 433 l0_%=: r0 = 0; \ 434 .byte 0xe5; /* may_goto */ \ 435 .byte 0; /* regs */ \ 436 .short 1; /* off 1 */ \ 437 .long 0; /* imm */ \ 438 gotol l0_%=; \ 439 r0 = 0; \ 440 exit; \ 441 " ::: __clobber_common); 442 } 443 #endif 444 445 SEC("socket") 446 __success __retval(0) 447 __naked void ja_and_may_goto_subprog(void) 448 { 449 asm volatile (" \ 450 call subprog_with_may_goto; \ 451 exit; \ 452 " ::: __clobber_all); 453 } 454 455 static __naked __noinline __used 456 void subprog_with_may_goto(void) 457 { 458 asm volatile (" \ 459 l0_%=: .byte 0xe5; /* may_goto */ \ 460 .byte 0; /* regs */ \ 461 .short 1; /* off 1 */ \ 462 .long 0; /* imm */ \ 463 goto l0_%=; \ 464 r0 = 0; \ 465 exit; \ 466 " ::: __clobber_all); 467 } 468 469 #define ARR_SZ 1000000 470 int zero; 471 char arr[ARR_SZ]; 472 473 SEC("socket") 474 __success __retval(0xd495cdc0) 475 int cond_break1(const void *ctx) 476 { 477 unsigned long i; 478 unsigned int sum = 0; 479 480 for (i = zero; i < ARR_SZ && can_loop; i++) 481 sum += i; 482 for (i = zero; i < ARR_SZ; i++) { 483 barrier_var(i); 484 sum += i + arr[i]; 485 cond_break; 486 } 487 488 return sum; 489 } 490 491 SEC("socket") 492 __success __retval(999000000) 493 int cond_break2(const void *ctx) 494 { 495 int i, j; 496 int sum = 0; 497 498 for (i = zero; i < 1000 && can_loop; i++) 499 for (j = zero; j < 1000; j++) { 500 sum += i + j; 501 cond_break; 502 } 503 return sum; 504 } 505 506 static __noinline int loop(void) 507 { 508 int i, sum = 0; 509 510 for (i = zero; i <= 1000000 && can_loop; i++) 511 sum += i; 512 513 return sum; 514 } 515 516 SEC("socket") 517 __success __retval(0x6a5a2920) 518 int cond_break3(const void *ctx) 519 { 520 return loop(); 521 } 522 523 SEC("socket") 524 __success __retval(1) 525 int cond_break4(const void *ctx) 526 { 527 int cnt = zero; 528 529 for (;;) { 530 /* should eventually break out of the loop */ 531 cond_break; 532 cnt++; 533 } 534 /* if we looped a bit, it's a success */ 535 return cnt > 1 ? 1 : 0; 536 } 537 538 static __noinline int static_subprog(void) 539 { 540 int cnt = zero; 541 542 for (;;) { 543 cond_break; 544 cnt++; 545 } 546 547 return cnt; 548 } 549 550 SEC("socket") 551 __success __retval(1) 552 int cond_break5(const void *ctx) 553 { 554 int cnt1 = zero, cnt2; 555 556 for (;;) { 557 cond_break; 558 cnt1++; 559 } 560 561 cnt2 = static_subprog(); 562 563 /* main and subprog have to loop a bit */ 564 return cnt1 > 1 && cnt2 > 1 ? 1 : 0; 565 } 566 567 #define ARR2_SZ 1000 568 SEC(".data.arr2") 569 char arr2[ARR2_SZ]; 570 571 SEC("socket") 572 __success __flag(BPF_F_TEST_STATE_FREQ) 573 int loop_inside_iter(const void *ctx) 574 { 575 struct bpf_iter_num it; 576 int *v, sum = 0; 577 __u64 i = 0; 578 579 bpf_iter_num_new(&it, 0, ARR2_SZ); 580 while ((v = bpf_iter_num_next(&it))) { 581 if (i < ARR2_SZ) 582 sum += arr2[i++]; 583 } 584 bpf_iter_num_destroy(&it); 585 return sum; 586 } 587 588 SEC("socket") 589 __success __flag(BPF_F_TEST_STATE_FREQ) 590 int loop_inside_iter_signed(const void *ctx) 591 { 592 struct bpf_iter_num it; 593 int *v, sum = 0; 594 long i = 0; 595 596 bpf_iter_num_new(&it, 0, ARR2_SZ); 597 while ((v = bpf_iter_num_next(&it))) { 598 if (i < ARR2_SZ && i >= 0) 599 sum += arr2[i++]; 600 } 601 bpf_iter_num_destroy(&it); 602 return sum; 603 } 604 605 volatile const int limit = ARR2_SZ; 606 607 SEC("socket") 608 __success __flag(BPF_F_TEST_STATE_FREQ) 609 int loop_inside_iter_volatile_limit(const void *ctx) 610 { 611 struct bpf_iter_num it; 612 int *v, sum = 0; 613 __u64 i = 0; 614 615 bpf_iter_num_new(&it, 0, ARR2_SZ); 616 while ((v = bpf_iter_num_next(&it))) { 617 if (i < limit) 618 sum += arr2[i++]; 619 } 620 bpf_iter_num_destroy(&it); 621 return sum; 622 } 623 624 #define ARR_LONG_SZ 1000 625 626 SEC(".data.arr_long") 627 long arr_long[ARR_LONG_SZ]; 628 629 SEC("socket") 630 __success 631 int test1(const void *ctx) 632 { 633 long i; 634 635 for (i = 0; i < ARR_LONG_SZ && can_loop; i++) 636 arr_long[i] = i; 637 return 0; 638 } 639 640 SEC("socket") 641 __success 642 int test2(const void *ctx) 643 { 644 __u64 i; 645 646 for (i = zero; i < ARR_LONG_SZ && can_loop; i++) { 647 barrier_var(i); 648 arr_long[i] = i; 649 } 650 return 0; 651 } 652 653 SEC(".data.arr_foo") 654 struct { 655 int a; 656 int b; 657 } arr_foo[ARR_LONG_SZ]; 658 659 SEC("socket") 660 __success 661 int test3(const void *ctx) 662 { 663 __u64 i; 664 665 for (i = zero; i < ARR_LONG_SZ && can_loop; i++) { 666 barrier_var(i); 667 arr_foo[i].a = i; 668 arr_foo[i].b = i; 669 } 670 return 0; 671 } 672 673 SEC("socket") 674 __success 675 int test4(const void *ctx) 676 { 677 long i; 678 679 for (i = zero + ARR_LONG_SZ - 1; i < ARR_LONG_SZ && i >= 0 && can_loop; i--) { 680 barrier_var(i); 681 arr_foo[i].a = i; 682 arr_foo[i].b = i; 683 } 684 return 0; 685 } 686 687 char buf[10] SEC(".data.buf"); 688 689 SEC("socket") 690 __description("check add const") 691 __success 692 __naked void check_add_const(void) 693 { 694 /* typical LLVM generated loop with may_goto */ 695 asm volatile (" \ 696 call %[bpf_ktime_get_ns]; \ 697 if r0 > 9 goto l1_%=; \ 698 l0_%=: r1 = %[buf]; \ 699 r2 = r0; \ 700 r1 += r2; \ 701 r3 = *(u8 *)(r1 +0); \ 702 .byte 0xe5; /* may_goto */ \ 703 .byte 0; /* regs */ \ 704 .short 4; /* off of l1_%=: */ \ 705 .long 0; /* imm */ \ 706 r0 = r2; \ 707 r0 += 1; \ 708 if r2 < 9 goto l0_%=; \ 709 exit; \ 710 l1_%=: r0 = 0; \ 711 exit; \ 712 " : 713 : __imm(bpf_ktime_get_ns), 714 __imm_ptr(buf) 715 : __clobber_common); 716 } 717 718 SEC("socket") 719 __failure 720 __msg("*(u8 *)(r7 +0) = r0") 721 __msg("invalid access to map value, value_size=10 off=10 size=1") 722 __naked void check_add_const_3regs(void) 723 { 724 asm volatile ( 725 "r6 = %[buf];" 726 "r7 = %[buf];" 727 "call %[bpf_ktime_get_ns];" 728 "r1 = r0;" /* link r0.id == r1.id == r2.id */ 729 "r2 = r0;" 730 "r1 += 1;" /* r1 == r0+1 */ 731 "r2 += 2;" /* r2 == r0+2 */ 732 "if r0 > 8 goto 1f;" /* r0 range [0, 8] */ 733 "r6 += r1;" /* r1 range [1, 9] */ 734 "r7 += r2;" /* r2 range [2, 10] */ 735 "*(u8 *)(r6 +0) = r0;" /* safe, within bounds */ 736 "*(u8 *)(r7 +0) = r0;" /* unsafe, out of bounds */ 737 "1: exit;" 738 : 739 : __imm(bpf_ktime_get_ns), 740 __imm_ptr(buf) 741 : __clobber_common); 742 } 743 744 SEC("socket") 745 __failure 746 __msg("*(u8 *)(r8 -1) = r0") 747 __msg("invalid access to map value, value_size=10 off=10 size=1") 748 __naked void check_add_const_3regs_2if(void) 749 { 750 asm volatile ( 751 "r6 = %[buf];" 752 "r7 = %[buf];" 753 "r8 = %[buf];" 754 "call %[bpf_ktime_get_ns];" 755 "if r0 < 2 goto 1f;" 756 "r1 = r0;" /* link r0.id == r1.id == r2.id */ 757 "r2 = r0;" 758 "r1 += 1;" /* r1 == r0+1 */ 759 "r2 += 2;" /* r2 == r0+2 */ 760 "if r2 > 11 goto 1f;" /* r2 range [0, 11] -> r0 range [-2, 9]; r1 range [-1, 10] */ 761 "if r0 s< 0 goto 1f;" /* r0 range [0, 9] -> r1 range [1, 10]; r2 range [2, 11]; */ 762 "r6 += r0;" /* r0 range [0, 9] */ 763 "r7 += r1;" /* r1 range [1, 10] */ 764 "r8 += r2;" /* r2 range [2, 11] */ 765 "*(u8 *)(r6 +0) = r0;" /* safe, within bounds */ 766 "*(u8 *)(r7 -1) = r0;" /* safe */ 767 "*(u8 *)(r8 -1) = r0;" /* unsafe */ 768 "1: exit;" 769 : 770 : __imm(bpf_ktime_get_ns), 771 __imm_ptr(buf) 772 : __clobber_common); 773 } 774 775 SEC("socket") 776 __failure 777 __flag(BPF_F_TEST_STATE_FREQ) 778 __naked void check_add_const_regsafe_off(void) 779 { 780 asm volatile ( 781 "r8 = %[buf];" 782 "call %[bpf_ktime_get_ns];" 783 "r6 = r0;" 784 "call %[bpf_ktime_get_ns];" 785 "r7 = r0;" 786 "call %[bpf_ktime_get_ns];" 787 "r1 = r0;" /* same ids for r1 and r0 */ 788 "if r6 > r7 goto 1f;" /* this jump can't be predicted */ 789 "r1 += 1;" /* r1.off == +1 */ 790 "goto 2f;" 791 "1: r1 += 100;" /* r1.off == +100 */ 792 "goto +0;" /* verify r1.off in regsafe() after this insn */ 793 "2: if r0 > 8 goto 3f;" /* r0 range [0,8], r1 range either [1,9] or [100,108]*/ 794 "r8 += r1;" 795 "*(u8 *)(r8 +0) = r0;" /* potentially unsafe, buf size is 10 */ 796 "3: exit;" 797 : 798 : __imm(bpf_ktime_get_ns), 799 __imm_ptr(buf) 800 : __clobber_common); 801 } 802 803 char _license[] SEC("license") = "GPL"; 804