1 // SPDX-License-Identifier: GPL-2.0 2 /* Converted from tools/testing/selftests/bpf/verifier/stack_ptr.c */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include <limits.h> 7 #include "bpf_misc.h" 8 9 #define MAX_ENTRIES 11 10 11 struct test_val { 12 unsigned int index; 13 int foo[MAX_ENTRIES]; 14 }; 15 16 struct { 17 __uint(type, BPF_MAP_TYPE_ARRAY); 18 __uint(max_entries, 1); 19 __type(key, int); 20 __type(value, struct test_val); 21 } map_array_48b SEC(".maps"); 22 23 SEC("socket") 24 __description("PTR_TO_STACK store/load") 25 __success __success_unpriv __retval(0xfaceb00c) 26 __naked void ptr_to_stack_store_load(void) 27 { 28 asm volatile (" \ 29 r1 = r10; \ 30 r1 += -10; \ 31 r0 = 0xfaceb00c; \ 32 *(u64*)(r1 + 2) = r0; \ 33 r0 = *(u64*)(r1 + 2); \ 34 exit; \ 35 " ::: __clobber_all); 36 } 37 38 SEC("socket") 39 __description("PTR_TO_STACK store/load - bad alignment on off") 40 __failure __msg("misaligned stack access off -8+2 size 8") 41 __failure_unpriv 42 __naked void load_bad_alignment_on_off(void) 43 { 44 asm volatile (" \ 45 r1 = r10; \ 46 r1 += -8; \ 47 r0 = 0xfaceb00c; \ 48 *(u64*)(r1 + 2) = r0; \ 49 r0 = *(u64*)(r1 + 2); \ 50 exit; \ 51 " ::: __clobber_all); 52 } 53 54 SEC("socket") 55 __description("PTR_TO_STACK store/load - bad alignment on reg") 56 __failure __msg("misaligned stack access off -10+8 size 8") 57 __failure_unpriv 58 __naked void load_bad_alignment_on_reg(void) 59 { 60 asm volatile (" \ 61 r1 = r10; \ 62 r1 += -10; \ 63 r0 = 0xfaceb00c; \ 64 *(u64*)(r1 + 8) = r0; \ 65 r0 = *(u64*)(r1 + 8); \ 66 exit; \ 67 " ::: __clobber_all); 68 } 69 70 SEC("socket") 71 __description("PTR_TO_STACK store/load - out of bounds low") 72 __failure __msg("invalid write to stack R1 off=-79992 size=8") 73 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 74 __naked void load_out_of_bounds_low(void) 75 { 76 asm volatile (" \ 77 r1 = r10; \ 78 r1 += -80000; \ 79 r0 = 0xfaceb00c; \ 80 *(u64*)(r1 + 8) = r0; \ 81 r0 = *(u64*)(r1 + 8); \ 82 exit; \ 83 " ::: __clobber_all); 84 } 85 86 SEC("socket") 87 __description("PTR_TO_STACK store/load - out of bounds high") 88 __failure __msg("invalid write to stack R1 off=0 size=8") 89 __failure_unpriv 90 __naked void load_out_of_bounds_high(void) 91 { 92 asm volatile (" \ 93 r1 = r10; \ 94 r1 += -8; \ 95 r0 = 0xfaceb00c; \ 96 *(u64*)(r1 + 8) = r0; \ 97 r0 = *(u64*)(r1 + 8); \ 98 exit; \ 99 " ::: __clobber_all); 100 } 101 102 SEC("socket") 103 __description("PTR_TO_STACK check high 1") 104 __success __success_unpriv __retval(42) 105 __naked void to_stack_check_high_1(void) 106 { 107 asm volatile (" \ 108 r1 = r10; \ 109 r1 += -1; \ 110 r0 = 42; \ 111 *(u8*)(r1 + 0) = r0; \ 112 r0 = *(u8*)(r1 + 0); \ 113 exit; \ 114 " ::: __clobber_all); 115 } 116 117 SEC("socket") 118 __description("PTR_TO_STACK check high 2") 119 __success __success_unpriv __retval(42) 120 __naked void to_stack_check_high_2(void) 121 { 122 asm volatile (" \ 123 r1 = r10; \ 124 r0 = 42; \ 125 *(u8*)(r1 - 1) = r0; \ 126 r0 = *(u8*)(r1 - 1); \ 127 exit; \ 128 " ::: __clobber_all); 129 } 130 131 SEC("socket") 132 __description("PTR_TO_STACK check high 3") 133 __success __failure_unpriv 134 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 135 __retval(42) 136 __naked void to_stack_check_high_3(void) 137 { 138 asm volatile (" \ 139 r1 = r10; \ 140 r1 += 0; \ 141 r0 = 42; \ 142 *(u8*)(r1 - 1) = r0; \ 143 r0 = *(u8*)(r1 - 1); \ 144 exit; \ 145 " ::: __clobber_all); 146 } 147 148 SEC("socket") 149 __description("PTR_TO_STACK check high 4") 150 __failure __msg("invalid write to stack R1 off=0 size=1") 151 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 152 __naked void to_stack_check_high_4(void) 153 { 154 asm volatile (" \ 155 r1 = r10; \ 156 r1 += 0; \ 157 r0 = 42; \ 158 *(u8*)(r1 + 0) = r0; \ 159 r0 = *(u8*)(r1 + 0); \ 160 exit; \ 161 " ::: __clobber_all); 162 } 163 164 SEC("socket") 165 __description("PTR_TO_STACK check high 5") 166 __failure __msg("invalid write to stack R1") 167 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 168 __naked void to_stack_check_high_5(void) 169 { 170 asm volatile (" \ 171 r1 = r10; \ 172 r1 += %[__imm_0]; \ 173 r0 = 42; \ 174 *(u8*)(r1 + 0) = r0; \ 175 r0 = *(u8*)(r1 + 0); \ 176 exit; \ 177 " : 178 : __imm_const(__imm_0, (1 << 29) - 1) 179 : __clobber_all); 180 } 181 182 SEC("socket") 183 __description("PTR_TO_STACK check high 6") 184 __failure __msg("invalid write to stack") 185 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 186 __naked void to_stack_check_high_6(void) 187 { 188 asm volatile (" \ 189 r1 = r10; \ 190 r1 += %[__imm_0]; \ 191 r0 = 42; \ 192 *(u8*)(r1 + %[shrt_max]) = r0; \ 193 r0 = *(u8*)(r1 + %[shrt_max]); \ 194 exit; \ 195 " : 196 : __imm_const(__imm_0, (1 << 29) - 1), 197 __imm_const(shrt_max, SHRT_MAX) 198 : __clobber_all); 199 } 200 201 SEC("socket") 202 __description("PTR_TO_STACK check high 7") 203 __failure __msg("fp pointer offset") 204 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 205 __naked void to_stack_check_high_7(void) 206 { 207 asm volatile (" \ 208 r1 = r10; \ 209 r1 += %[__imm_0]; \ 210 r1 += %[__imm_0]; \ 211 r0 = 42; \ 212 *(u8*)(r1 + %[shrt_max]) = r0; \ 213 r0 = *(u8*)(r1 + %[shrt_max]); \ 214 exit; \ 215 " : 216 : __imm_const(__imm_0, (1 << 29) - 1), 217 __imm_const(shrt_max, SHRT_MAX) 218 : __clobber_all); 219 } 220 221 SEC("socket") 222 __description("PTR_TO_STACK check low 1") 223 __success __success_unpriv __retval(42) 224 __naked void to_stack_check_low_1(void) 225 { 226 asm volatile (" \ 227 r1 = r10; \ 228 r1 += -512; \ 229 r0 = 42; \ 230 *(u8*)(r1 + 0) = r0; \ 231 r0 = *(u8*)(r1 + 0); \ 232 exit; \ 233 " ::: __clobber_all); 234 } 235 236 SEC("socket") 237 __description("PTR_TO_STACK check low 2") 238 __load_if_no_large_stack() 239 __success __failure_unpriv 240 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 241 __retval(42) 242 __naked void to_stack_check_low_2(void) 243 { 244 asm volatile (" \ 245 r1 = r10; \ 246 r1 += -513; \ 247 r0 = 42; \ 248 *(u8*)(r1 + 1) = r0; \ 249 r0 = *(u8*)(r1 + 1); \ 250 exit; \ 251 " ::: __clobber_all); 252 } 253 254 SEC("socket") 255 __description("PTR_TO_STACK check low 2, large stack") 256 __load_if_large_stack() 257 __success __failure_unpriv 258 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 259 __retval(42) 260 __naked void to_stack_check_low_2_large(void) 261 { 262 asm volatile (" \ 263 r1 = r10; \ 264 r1 += -2049; \ 265 r0 = 42; \ 266 *(u8*)(r1 + 1) = r0; \ 267 r0 = *(u8*)(r1 + 1); \ 268 exit; \ 269 " ::: __clobber_all); 270 } 271 272 SEC("socket") 273 __description("PTR_TO_STACK check low 3") 274 __load_if_no_large_stack() 275 __failure __msg("invalid write to stack R1 off=-513 size=1") 276 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 277 __naked void to_stack_check_low_3(void) 278 { 279 asm volatile (" \ 280 r1 = r10; \ 281 r1 += -513; \ 282 r0 = 42; \ 283 *(u8*)(r1 + 0) = r0; \ 284 r0 = *(u8*)(r1 + 0); \ 285 exit; \ 286 " ::: __clobber_all); 287 } 288 289 SEC("socket") 290 __description("PTR_TO_STACK check low 3, large stack") 291 __load_if_large_stack() 292 __failure __msg("invalid write to stack R1 off=-2049 size=1") 293 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 294 __naked void to_stack_check_low_3_large(void) 295 { 296 asm volatile (" \ 297 r1 = r10; \ 298 r1 += -2049; \ 299 r0 = 42; \ 300 *(u8*)(r1 + 0) = r0; \ 301 r0 = *(u8*)(r1 + 0); \ 302 exit; \ 303 " ::: __clobber_all); 304 } 305 306 SEC("socket") 307 __description("PTR_TO_STACK check low 4") 308 __failure __msg("math between fp pointer") 309 __failure_unpriv 310 __naked void to_stack_check_low_4(void) 311 { 312 asm volatile (" \ 313 r1 = r10; \ 314 r1 += %[int_min]; \ 315 r0 = 42; \ 316 *(u8*)(r1 + 0) = r0; \ 317 r0 = *(u8*)(r1 + 0); \ 318 exit; \ 319 " : 320 : __imm_const(int_min, INT_MIN) 321 : __clobber_all); 322 } 323 324 SEC("socket") 325 __description("PTR_TO_STACK check low 5") 326 __failure __msg("invalid write to stack") 327 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 328 __naked void to_stack_check_low_5(void) 329 { 330 asm volatile (" \ 331 r1 = r10; \ 332 r1 += %[__imm_0]; \ 333 r0 = 42; \ 334 *(u8*)(r1 + 0) = r0; \ 335 r0 = *(u8*)(r1 + 0); \ 336 exit; \ 337 " : 338 : __imm_const(__imm_0, -((1 << 29) - 1)) 339 : __clobber_all); 340 } 341 342 SEC("socket") 343 __description("PTR_TO_STACK check low 6") 344 __failure __msg("invalid write to stack") 345 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 346 __naked void to_stack_check_low_6(void) 347 { 348 asm volatile (" \ 349 r1 = r10; \ 350 r1 += %[__imm_0]; \ 351 r0 = 42; \ 352 *(u8*)(r1 %[shrt_min]) = r0; \ 353 r0 = *(u8*)(r1 %[shrt_min]); \ 354 exit; \ 355 " : 356 : __imm_const(__imm_0, -((1 << 29) - 1)), 357 __imm_const(shrt_min, SHRT_MIN) 358 : __clobber_all); 359 } 360 361 SEC("socket") 362 __description("PTR_TO_STACK check low 7") 363 __failure __msg("fp pointer offset") 364 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 365 __naked void to_stack_check_low_7(void) 366 { 367 asm volatile (" \ 368 r1 = r10; \ 369 r1 += %[__imm_0]; \ 370 r1 += %[__imm_0]; \ 371 r0 = 42; \ 372 *(u8*)(r1 %[shrt_min]) = r0; \ 373 r0 = *(u8*)(r1 %[shrt_min]); \ 374 exit; \ 375 " : 376 : __imm_const(__imm_0, -((1 << 29) - 1)), 377 __imm_const(shrt_min, SHRT_MIN) 378 : __clobber_all); 379 } 380 381 SEC("socket") 382 __description("PTR_TO_STACK mixed reg/k, 1") 383 __success __success_unpriv __retval(42) 384 __naked void stack_mixed_reg_k_1(void) 385 { 386 asm volatile (" \ 387 r1 = r10; \ 388 r1 += -3; \ 389 r2 = -3; \ 390 r1 += r2; \ 391 r0 = 42; \ 392 *(u8*)(r1 + 0) = r0; \ 393 r0 = *(u8*)(r1 + 0); \ 394 exit; \ 395 " ::: __clobber_all); 396 } 397 398 SEC("socket") 399 __description("PTR_TO_STACK mixed reg/k, 2") 400 __success __success_unpriv __retval(42) 401 __naked void stack_mixed_reg_k_2(void) 402 { 403 asm volatile (" \ 404 r0 = 0; \ 405 *(u64*)(r10 - 8) = r0; \ 406 r0 = 0; \ 407 *(u64*)(r10 - 16) = r0; \ 408 r1 = r10; \ 409 r1 += -3; \ 410 r2 = -3; \ 411 r1 += r2; \ 412 r0 = 42; \ 413 *(u8*)(r1 + 0) = r0; \ 414 r5 = r10; \ 415 r0 = *(u8*)(r5 - 6); \ 416 exit; \ 417 " ::: __clobber_all); 418 } 419 420 SEC("socket") 421 __description("PTR_TO_STACK mixed reg/k, 3") 422 __success __success_unpriv __retval(-3) 423 __naked void stack_mixed_reg_k_3(void) 424 { 425 asm volatile (" \ 426 r1 = r10; \ 427 r1 += -3; \ 428 r2 = -3; \ 429 r1 += r2; \ 430 r0 = 42; \ 431 *(u8*)(r1 + 0) = r0; \ 432 r0 = r2; \ 433 exit; \ 434 " ::: __clobber_all); 435 } 436 437 SEC("socket") 438 __description("PTR_TO_STACK reg") 439 __success __success_unpriv __retval(42) 440 __naked void ptr_to_stack_reg(void) 441 { 442 asm volatile (" \ 443 r1 = r10; \ 444 r2 = -3; \ 445 r1 += r2; \ 446 r0 = 42; \ 447 *(u8*)(r1 + 0) = r0; \ 448 r0 = *(u8*)(r1 + 0); \ 449 exit; \ 450 " ::: __clobber_all); 451 } 452 453 SEC("socket") 454 __description("stack pointer arithmetic") 455 __success __success_unpriv __retval(0) 456 __naked void stack_pointer_arithmetic(void) 457 { 458 asm volatile (" \ 459 r1 = 4; \ 460 goto l0_%=; \ 461 l0_%=: r7 = r10; \ 462 r7 += -10; \ 463 r7 += -10; \ 464 r2 = r7; \ 465 r2 += r1; \ 466 r0 = 0; \ 467 *(u32*)(r2 + 4) = r0; \ 468 r2 = r7; \ 469 r2 += 8; \ 470 r0 = 0; \ 471 *(u32*)(r2 + 4) = r0; \ 472 r0 = 0; \ 473 exit; \ 474 " ::: __clobber_all); 475 } 476 477 SEC("tc") 478 __description("store PTR_TO_STACK in R10 to array map using BPF_B") 479 __success __retval(42) 480 __naked void array_map_using_bpf_b(void) 481 { 482 asm volatile (" \ 483 /* Load pointer to map. */ \ 484 r2 = r10; \ 485 r2 += -8; \ 486 r1 = 0; \ 487 *(u64*)(r2 + 0) = r1; \ 488 r1 = %[map_array_48b] ll; \ 489 call %[bpf_map_lookup_elem]; \ 490 if r0 != 0 goto l0_%=; \ 491 r0 = 2; \ 492 exit; \ 493 l0_%=: r1 = r0; \ 494 /* Copy R10 to R9. */ \ 495 r9 = r10; \ 496 /* Pollute other registers with unaligned values. */\ 497 r2 = -1; \ 498 r3 = -1; \ 499 r4 = -1; \ 500 r5 = -1; \ 501 r6 = -1; \ 502 r7 = -1; \ 503 r8 = -1; \ 504 /* Store both R9 and R10 with BPF_B and read back. */\ 505 *(u8*)(r1 + 0) = r10; \ 506 r2 = *(u8*)(r1 + 0); \ 507 *(u8*)(r1 + 0) = r9; \ 508 r3 = *(u8*)(r1 + 0); \ 509 /* Should read back as same value. */ \ 510 if r2 == r3 goto l1_%=; \ 511 r0 = 1; \ 512 exit; \ 513 l1_%=: r0 = 42; \ 514 exit; \ 515 " : 516 : __imm(bpf_map_lookup_elem), 517 __imm_addr(map_array_48b) 518 : __clobber_all); 519 } 520 521 SEC("socket") 522 __description("PTR_TO_STACK stack size > 512") 523 __load_if_no_large_stack() 524 __failure __msg("invalid write to stack R1 off=-520 size=8") 525 __naked void stack_check_size_gt_512(void) 526 { 527 asm volatile (" \ 528 r1 = r10; \ 529 r1 += -520; \ 530 r0 = 42; \ 531 *(u64*)(r1 + 0) = r0; \ 532 exit; \ 533 " ::: __clobber_all); 534 } 535 536 SEC("socket") 537 __description("PTR_TO_STACK stack size > 2048") 538 __load_if_large_stack() 539 __failure __msg("invalid write to stack R1 off=-2056 size=8") 540 __naked void stack_check_size_gt_2048(void) 541 { 542 asm volatile (" \ 543 r1 = r10; \ 544 r1 += -2056; \ 545 r0 = 42; \ 546 *(u64*)(r1 + 0) = r0; \ 547 exit; \ 548 " ::: __clobber_all); 549 } 550 551 #ifdef __BPF_FEATURE_MAY_GOTO 552 SEC("socket") 553 __description("PTR_TO_STACK stack size 512 with may_goto with jit") 554 __load_if_JITed() 555 __success __retval(42) 556 __naked void stack_check_size_512_with_may_goto_jit(void) 557 { 558 asm volatile (" \ 559 r1 = r10; \ 560 r1 += -512; \ 561 r0 = 42; \ 562 *(u32*)(r1 + 0) = r0; \ 563 may_goto l0_%=; \ 564 r2 = 100; \ 565 l0_%=: \ 566 exit; \ 567 " ::: __clobber_all); 568 } 569 570 SEC("socket") 571 __description("PTR_TO_STACK stack size 512 with may_goto without jit") 572 __load_if_no_JITed() 573 __failure __msg("stack size 520(extra 8) is too large") 574 __naked void stack_check_size_512_with_may_goto(void) 575 { 576 asm volatile (" \ 577 r1 = r10; \ 578 r1 += -512; \ 579 r0 = 42; \ 580 *(u32*)(r1 + 0) = r0; \ 581 may_goto l0_%=; \ 582 r2 = 100; \ 583 l0_%=: \ 584 exit; \ 585 " ::: __clobber_all); 586 } 587 #endif 588 589 /* 590 * Verify that old PTR_TO_STACK state is considered a super-set of 591 * new PTR_TO_STACK state when new variable range is a sub-range 592 * of the old range, e.g. old [-72, -16] vs new [-64, -16]. 593 */ 594 SEC("socket") 595 __success 596 __log_level(2) 597 __flag(BPF_F_TEST_STATE_FREQ) 598 /* 599 * r7 is widened to [-72, -16] at the loop header (insn 3), 600 * the loop body sees [-64, -8] after 'r7 += 8' 601 */ 602 __msg("loop header at 3, widening r7 to -72..-16 step 8") 603 __msg("R7=fp(smin=smin32=-64,smax=smax32=-8") 604 /* 605 * back-edge state is clamped to the remaining iterations and pruned 606 * at the header, because [-64, -16] is within the widened [-72, -16] 607 */ 608 __msg("loop header at 3, clamping r7 to -64..-16 step 8") 609 __msg("from 5 to 3: safe") 610 __msg("processed 9 insns") 611 __naked void stack_ptr_subrange_in_loop(void) 612 { 613 asm volatile (" \ 614 r7 = r10; \ 615 r7 += -72; \ 616 r6 = 0; \ 617 r7 += 8; \ 618 r6 += 1; \ 619 if r6 < 8 goto -3; \ 620 r0 = 0; \ 621 exit; \ 622 " ::: __clobber_all); 623 } 624 625 char _license[] SEC("license") = "GPL"; 626