1 // SPDX-License-Identifier: GPL-2.0 2 /* Converted from tools/testing/selftests/bpf/verifier/unpriv.c */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include "../../../include/linux/filter.h" 7 #include "bpf_misc.h" 8 9 extern const int bpf_prog_active __ksym; 10 11 #define BPF_SK_LOOKUP(func) \ 12 /* struct bpf_sock_tuple tuple = {} */ \ 13 "r2 = 0;" \ 14 "*(u32*)(r10 - 8) = r2;" \ 15 "*(u64*)(r10 - 16) = r2;" \ 16 "*(u64*)(r10 - 24) = r2;" \ 17 "*(u64*)(r10 - 32) = r2;" \ 18 "*(u64*)(r10 - 40) = r2;" \ 19 "*(u64*)(r10 - 48) = r2;" \ 20 /* sk = func(ctx, &tuple, sizeof tuple, 0, 0) */ \ 21 "r2 = r10;" \ 22 "r2 += -48;" \ 23 "r3 = %[sizeof_bpf_sock_tuple];"\ 24 "r4 = 0;" \ 25 "r5 = 0;" \ 26 "call %[" #func "];" 27 28 struct { 29 __uint(type, BPF_MAP_TYPE_HASH); 30 __uint(max_entries, 1); 31 __type(key, long long); 32 __type(value, long long); 33 } map_hash_8b SEC(".maps"); 34 35 void dummy_prog_42_socket(void); 36 void dummy_prog_24_socket(void); 37 void dummy_prog_loop1_socket(void); 38 39 struct { 40 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 41 __uint(max_entries, 4); 42 __uint(key_size, sizeof(int)); 43 __array(values, void (void)); 44 } map_prog1_socket SEC(".maps") = { 45 .values = { 46 [0] = (void *)&dummy_prog_42_socket, 47 [1] = (void *)&dummy_prog_loop1_socket, 48 [2] = (void *)&dummy_prog_24_socket, 49 }, 50 }; 51 52 SEC("socket") 53 __auxiliary __auxiliary_unpriv 54 __naked void dummy_prog_42_socket(void) 55 { 56 asm volatile ("r0 = 42; exit;"); 57 } 58 59 SEC("socket") 60 __auxiliary __auxiliary_unpriv 61 __naked void dummy_prog_24_socket(void) 62 { 63 asm volatile ("r0 = 24; exit;"); 64 } 65 66 SEC("socket") 67 __auxiliary __auxiliary_unpriv 68 __naked void dummy_prog_loop1_socket(void) 69 { 70 asm volatile (" \ 71 r3 = 1; \ 72 r2 = %[map_prog1_socket] ll; \ 73 call %[bpf_tail_call]; \ 74 r0 = 41; \ 75 exit; \ 76 " : 77 : __imm(bpf_tail_call), 78 __imm_addr(map_prog1_socket) 79 : __clobber_all); 80 } 81 82 SEC("socket") 83 __description("unpriv: pseudo btf id log masks address") 84 __success_unpriv 85 __msg_unpriv("0: (18) r1 = 0x0") 86 __not_msg_unpriv("0: (18) r1 = 0x{{[1-9a-f][0-9a-f]*}}") 87 __retval_unpriv(0) 88 __log_level(2) 89 __naked void pseudo_btf_id_log_masks_address(void) 90 { 91 asm volatile ("r1 = %[bpf_prog_active] ll;" 92 "r0 = 0;" 93 "exit;" 94 : 95 : __imm_addr(bpf_prog_active) 96 : __clobber_all); 97 } 98 99 static int pseudo_func_callback(__u32 index, void *ctx) 100 { 101 return 0; 102 } 103 104 SEC("socket") 105 __description("unpriv: pseudo function policy diagnostic") 106 __success __failure_unpriv 107 __msg_unpriv("loading/calling other bpf or kernel functions") 108 __not_msg_unpriv("BPF-to-BPF function call") 109 __msg_unpriv("policy check failed for BPF function reference") 110 __msg_unpriv("avoid BPF function references in unprivileged") 111 int unpriv_pseudo_func_policy(void *ctx) 112 { 113 bpf_loop(1, pseudo_func_callback, NULL, 0); 114 return 0; 115 } 116 117 SEC("socket") 118 __description("unpriv: return pointer") 119 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 120 __retval(POINTER_VALUE) 121 __naked void unpriv_return_pointer(void) 122 { 123 asm volatile (" \ 124 r0 = r10; \ 125 exit; \ 126 " ::: __clobber_all); 127 } 128 129 SEC("socket") 130 __description("unpriv: add const to pointer") 131 __success __success_unpriv __retval(0) 132 __naked void unpriv_add_const_to_pointer(void) 133 { 134 asm volatile (" \ 135 r1 += 8; \ 136 r0 = 0; \ 137 exit; \ 138 " ::: __clobber_all); 139 } 140 141 SEC("socket") 142 __description("unpriv: add pointer to pointer") 143 __failure __msg("R1 pointer += pointer") 144 __failure_unpriv 145 __naked void unpriv_add_pointer_to_pointer(void) 146 { 147 asm volatile (" \ 148 r1 += r10; \ 149 r0 = 0; \ 150 exit; \ 151 " ::: __clobber_all); 152 } 153 154 SEC("socket") 155 __description("unpriv: neg pointer") 156 __success __failure_unpriv __msg_unpriv("R1 pointer arithmetic") 157 __retval(0) 158 __naked void unpriv_neg_pointer(void) 159 { 160 asm volatile (" \ 161 r1 = -r1; \ 162 r0 = 0; \ 163 exit; \ 164 " ::: __clobber_all); 165 } 166 167 SEC("socket") 168 __description("unpriv: cmp pointer with const") 169 __success __failure_unpriv __msg_unpriv("R1 pointer comparison") 170 __retval(0) 171 __naked void unpriv_cmp_pointer_with_const(void) 172 { 173 asm volatile (" \ 174 if r1 == 0 goto l0_%=; \ 175 l0_%=: r0 = 0; \ 176 exit; \ 177 " ::: __clobber_all); 178 } 179 180 SEC("socket") 181 __description("unpriv: cmp pointer with pointer") 182 __success __failure_unpriv __msg_unpriv("R10 pointer comparison") 183 __retval(0) 184 __naked void unpriv_cmp_pointer_with_pointer(void) 185 { 186 asm volatile (" \ 187 if r1 == r10 goto l0_%=; \ 188 l0_%=: r0 = 0; \ 189 exit; \ 190 " ::: __clobber_all); 191 } 192 193 SEC("tracepoint") 194 __description("unpriv: check that printk is disallowed") 195 __success 196 __naked void check_that_printk_is_disallowed(void) 197 { 198 asm volatile (" \ 199 r1 = 0; \ 200 *(u64*)(r10 - 8) = r1; \ 201 r1 = r10; \ 202 r1 += -8; \ 203 r2 = 8; \ 204 r3 = r1; \ 205 call %[bpf_trace_printk]; \ 206 r0 = 0; \ 207 exit; \ 208 " : 209 : __imm(bpf_trace_printk) 210 : __clobber_all); 211 } 212 213 SEC("socket") 214 __description("unpriv: pass pointer to helper function") 215 __success __failure_unpriv __msg_unpriv("R4 leaks addr") 216 __retval(0) 217 __naked void pass_pointer_to_helper_function(void) 218 { 219 asm volatile (" \ 220 r1 = 0; \ 221 *(u64*)(r10 - 8) = r1; \ 222 r2 = r10; \ 223 r2 += -8; \ 224 r1 = %[map_hash_8b] ll; \ 225 r3 = r2; \ 226 r4 = r2; \ 227 call %[bpf_map_update_elem]; \ 228 r0 = 0; \ 229 exit; \ 230 " : 231 : __imm(bpf_map_update_elem), 232 __imm_addr(map_hash_8b) 233 : __clobber_all); 234 } 235 236 SEC("socket") 237 __description("unpriv: indirectly pass pointer on stack to helper function") 238 __success __failure_unpriv 239 __msg_unpriv("invalid read from stack R2 off -8+0 size 8") 240 __retval(0) 241 __naked void on_stack_to_helper_function(void) 242 { 243 asm volatile (" \ 244 *(u64*)(r10 - 8) = r10; \ 245 r2 = r10; \ 246 r2 += -8; \ 247 r1 = %[map_hash_8b] ll; \ 248 call %[bpf_map_lookup_elem]; \ 249 r0 = 0; \ 250 exit; \ 251 " : 252 : __imm(bpf_map_lookup_elem), 253 __imm_addr(map_hash_8b) 254 : __clobber_all); 255 } 256 257 SEC("socket") 258 __description("unpriv: mangle pointer on stack 1") 259 __success __failure_unpriv __msg_unpriv("attempt to corrupt spilled") 260 __retval(0) 261 __naked void mangle_pointer_on_stack_1(void) 262 { 263 asm volatile (" \ 264 *(u64*)(r10 - 8) = r10; \ 265 r0 = 0; \ 266 *(u32*)(r10 - 8) = r0; \ 267 r0 = 0; \ 268 exit; \ 269 " ::: __clobber_all); 270 } 271 272 SEC("socket") 273 __description("unpriv: mangle pointer on stack 2") 274 __success __failure_unpriv __msg_unpriv("attempt to corrupt spilled") 275 __retval(0) 276 __naked void mangle_pointer_on_stack_2(void) 277 { 278 asm volatile (" \ 279 *(u64*)(r10 - 8) = r10; \ 280 r0 = 0; \ 281 *(u8*)(r10 - 1) = r0; \ 282 r0 = 0; \ 283 exit; \ 284 " ::: __clobber_all); 285 } 286 287 SEC("socket") 288 __description("unpriv: read pointer from stack in small chunks") 289 __failure __msg("invalid size") 290 __failure_unpriv 291 __naked void from_stack_in_small_chunks(void) 292 { 293 asm volatile (" \ 294 *(u64*)(r10 - 8) = r10; \ 295 r0 = *(u32*)(r10 - 8); \ 296 r0 = 0; \ 297 exit; \ 298 " ::: __clobber_all); 299 } 300 301 SEC("socket") 302 __description("unpriv: write pointer into ctx") 303 __failure __msg("invalid bpf_context access") 304 __failure_unpriv __msg_unpriv("R1 leaks addr") 305 __naked void unpriv_write_pointer_into_ctx(void) 306 { 307 asm volatile (" \ 308 *(u64*)(r1 + 0) = r1; \ 309 r0 = 0; \ 310 exit; \ 311 " ::: __clobber_all); 312 } 313 314 SEC("socket") 315 __description("unpriv: spill/fill of ctx") 316 __success __success_unpriv __retval(0) 317 __naked void unpriv_spill_fill_of_ctx(void) 318 { 319 asm volatile (" \ 320 r6 = r10; \ 321 r6 += -8; \ 322 *(u64*)(r6 + 0) = r1; \ 323 r1 = *(u64*)(r6 + 0); \ 324 r0 = 0; \ 325 exit; \ 326 " ::: __clobber_all); 327 } 328 329 SEC("tc") 330 __description("unpriv: spill/fill of ctx 2") 331 __success __retval(0) 332 __naked void spill_fill_of_ctx_2(void) 333 { 334 asm volatile (" \ 335 r6 = r10; \ 336 r6 += -8; \ 337 *(u64*)(r6 + 0) = r1; \ 338 r1 = *(u64*)(r6 + 0); \ 339 call %[bpf_get_hash_recalc]; \ 340 r0 = 0; \ 341 exit; \ 342 " : 343 : __imm(bpf_get_hash_recalc) 344 : __clobber_all); 345 } 346 347 SEC("tc") 348 __description("unpriv: spill/fill of ctx 3") 349 __failure __msg("R1 type=fp expected=ctx") 350 __naked void spill_fill_of_ctx_3(void) 351 { 352 asm volatile (" \ 353 r6 = r10; \ 354 r6 += -8; \ 355 *(u64*)(r6 + 0) = r1; \ 356 *(u64*)(r6 + 0) = r10; \ 357 r1 = *(u64*)(r6 + 0); \ 358 call %[bpf_get_hash_recalc]; \ 359 exit; \ 360 " : 361 : __imm(bpf_get_hash_recalc) 362 : __clobber_all); 363 } 364 365 SEC("tc") 366 __description("unpriv: spill/fill of ctx 4") 367 __failure __msg("R1 type=scalar expected=ctx") 368 __naked void spill_fill_of_ctx_4(void) 369 { 370 asm volatile (" \ 371 r6 = r10; \ 372 r6 += -8; \ 373 *(u64*)(r6 + 0) = r1; \ 374 r0 = 1; \ 375 lock *(u64 *)(r10 - 8) += r0; \ 376 r1 = *(u64*)(r6 + 0); \ 377 call %[bpf_get_hash_recalc]; \ 378 exit; \ 379 " : 380 : __imm(bpf_get_hash_recalc) 381 : __clobber_all); 382 } 383 384 SEC("tc") 385 __description("unpriv: spill/fill of different pointers stx") 386 __failure __msg("same insn cannot be used with different pointers") 387 __naked void fill_of_different_pointers_stx(void) 388 { 389 asm volatile (" \ 390 r3 = 42; \ 391 r6 = r10; \ 392 r6 += -8; \ 393 if r1 == 0 goto l0_%=; \ 394 r2 = r10; \ 395 r2 += -16; \ 396 *(u64*)(r6 + 0) = r2; \ 397 l0_%=: if r1 != 0 goto l1_%=; \ 398 *(u64*)(r6 + 0) = r1; \ 399 l1_%=: r1 = *(u64*)(r6 + 0); \ 400 *(u32*)(r1 + %[__sk_buff_mark]) = r3; \ 401 r0 = 0; \ 402 exit; \ 403 " : 404 : __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)) 405 : __clobber_all); 406 } 407 408 /* Same as above, but use BPF_ST_MEM to save 42 409 * instead of BPF_STX_MEM. 410 */ 411 SEC("tc") 412 __description("unpriv: spill/fill of different pointers st") 413 __failure __msg("same insn cannot be used with different pointers") 414 __naked void fill_of_different_pointers_st(void) 415 { 416 asm volatile (" \ 417 r6 = r10; \ 418 r6 += -8; \ 419 if r1 == 0 goto l0_%=; \ 420 r2 = r10; \ 421 r2 += -16; \ 422 *(u64*)(r6 + 0) = r2; \ 423 l0_%=: if r1 != 0 goto l1_%=; \ 424 *(u64*)(r6 + 0) = r1; \ 425 l1_%=: r1 = *(u64*)(r6 + 0); \ 426 .8byte %[st_mem]; \ 427 r0 = 0; \ 428 exit; \ 429 " : 430 : __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)), 431 __imm_insn(st_mem, 432 BPF_ST_MEM(BPF_W, BPF_REG_1, offsetof(struct __sk_buff, mark), 42)) 433 : __clobber_all); 434 } 435 436 SEC("tc") 437 __description("unpriv: spill/fill of different pointers stx - ctx and sock") 438 __failure __msg("type=ctx expected=sock") 439 __naked void pointers_stx_ctx_and_sock(void) 440 { 441 asm volatile (" \ 442 r8 = r1; \ 443 /* struct bpf_sock *sock = bpf_sock_lookup(...); */\ 444 " BPF_SK_LOOKUP(bpf_sk_lookup_tcp) 445 " r2 = r0; \ 446 /* u64 foo; */ \ 447 /* void *target = &foo; */ \ 448 r6 = r10; \ 449 r6 += -8; \ 450 r1 = r8; \ 451 /* if (skb == NULL) *target = sock; */ \ 452 if r1 == 0 goto l0_%=; \ 453 *(u64*)(r6 + 0) = r2; \ 454 l0_%=: /* else *target = skb; */ \ 455 if r1 != 0 goto l1_%=; \ 456 *(u64*)(r6 + 0) = r1; \ 457 l1_%=: /* struct __sk_buff *skb = *target; */ \ 458 r1 = *(u64*)(r6 + 0); \ 459 /* skb->mark = 42; */ \ 460 r3 = 42; \ 461 *(u32*)(r1 + %[__sk_buff_mark]) = r3; \ 462 /* if (sk) bpf_sk_release(sk) */ \ 463 if r1 == 0 goto l2_%=; \ 464 call %[bpf_sk_release]; \ 465 l2_%=: r0 = 0; \ 466 exit; \ 467 " : 468 : __imm(bpf_sk_lookup_tcp), 469 __imm(bpf_sk_release), 470 __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)), 471 __imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple)) 472 : __clobber_all); 473 } 474 475 SEC("tc") 476 __description("unpriv: spill/fill of different pointers stx - leak sock") 477 __failure 478 //.errstr = "same insn cannot be used with different pointers", 479 __msg("Unreleased reference") 480 __naked void different_pointers_stx_leak_sock(void) 481 { 482 asm volatile (" \ 483 r8 = r1; \ 484 /* struct bpf_sock *sock = bpf_sock_lookup(...); */\ 485 " BPF_SK_LOOKUP(bpf_sk_lookup_tcp) 486 " r2 = r0; \ 487 /* u64 foo; */ \ 488 /* void *target = &foo; */ \ 489 r6 = r10; \ 490 r6 += -8; \ 491 r1 = r8; \ 492 /* if (skb == NULL) *target = sock; */ \ 493 if r1 == 0 goto l0_%=; \ 494 *(u64*)(r6 + 0) = r2; \ 495 l0_%=: /* else *target = skb; */ \ 496 if r1 != 0 goto l1_%=; \ 497 *(u64*)(r6 + 0) = r1; \ 498 l1_%=: /* struct __sk_buff *skb = *target; */ \ 499 r1 = *(u64*)(r6 + 0); \ 500 /* skb->mark = 42; */ \ 501 r3 = 42; \ 502 *(u32*)(r1 + %[__sk_buff_mark]) = r3; \ 503 exit; \ 504 " : 505 : __imm(bpf_sk_lookup_tcp), 506 __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)), 507 __imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple)) 508 : __clobber_all); 509 } 510 511 SEC("tc") 512 __description("unpriv: spill/fill of different pointers stx - sock and ctx (read)") 513 __failure __msg("same insn cannot be used with different pointers") 514 __naked void stx_sock_and_ctx_read(void) 515 { 516 asm volatile (" \ 517 r8 = r1; \ 518 /* struct bpf_sock *sock = bpf_sock_lookup(...); */\ 519 " BPF_SK_LOOKUP(bpf_sk_lookup_tcp) 520 " r2 = r0; \ 521 /* u64 foo; */ \ 522 /* void *target = &foo; */ \ 523 r6 = r10; \ 524 r6 += -8; \ 525 r1 = r8; \ 526 /* if (skb) *target = skb */ \ 527 if r1 == 0 goto l0_%=; \ 528 *(u64*)(r6 + 0) = r1; \ 529 l0_%=: /* else *target = sock */ \ 530 if r1 != 0 goto l1_%=; \ 531 *(u64*)(r6 + 0) = r2; \ 532 l1_%=: /* struct bpf_sock *sk = *target; */ \ 533 r1 = *(u64*)(r6 + 0); \ 534 /* if (sk) u32 foo = sk->mark; bpf_sk_release(sk); */\ 535 if r1 == 0 goto l2_%=; \ 536 r3 = *(u32*)(r1 + %[bpf_sock_mark]); \ 537 call %[bpf_sk_release]; \ 538 l2_%=: r0 = 0; \ 539 exit; \ 540 " : 541 : __imm(bpf_sk_lookup_tcp), 542 __imm(bpf_sk_release), 543 __imm_const(bpf_sock_mark, offsetof(struct bpf_sock, mark)), 544 __imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple)) 545 : __clobber_all); 546 } 547 548 SEC("tc") 549 __description("unpriv: spill/fill of different pointers stx - sock and ctx (write)") 550 __failure 551 //.errstr = "same insn cannot be used with different pointers", 552 __msg("cannot write into sock") 553 __naked void stx_sock_and_ctx_write(void) 554 { 555 asm volatile (" \ 556 r8 = r1; \ 557 /* struct bpf_sock *sock = bpf_sock_lookup(...); */\ 558 " BPF_SK_LOOKUP(bpf_sk_lookup_tcp) 559 " r2 = r0; \ 560 /* u64 foo; */ \ 561 /* void *target = &foo; */ \ 562 r6 = r10; \ 563 r6 += -8; \ 564 r1 = r8; \ 565 /* if (skb) *target = skb */ \ 566 if r1 == 0 goto l0_%=; \ 567 *(u64*)(r6 + 0) = r1; \ 568 l0_%=: /* else *target = sock */ \ 569 if r1 != 0 goto l1_%=; \ 570 *(u64*)(r6 + 0) = r2; \ 571 l1_%=: /* struct bpf_sock *sk = *target; */ \ 572 r1 = *(u64*)(r6 + 0); \ 573 /* if (sk) sk->mark = 42; bpf_sk_release(sk); */\ 574 if r1 == 0 goto l2_%=; \ 575 r3 = 42; \ 576 *(u32*)(r1 + %[bpf_sock_mark]) = r3; \ 577 call %[bpf_sk_release]; \ 578 l2_%=: r0 = 0; \ 579 exit; \ 580 " : 581 : __imm(bpf_sk_lookup_tcp), 582 __imm(bpf_sk_release), 583 __imm_const(bpf_sock_mark, offsetof(struct bpf_sock, mark)), 584 __imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple)) 585 : __clobber_all); 586 } 587 588 SEC("socket") 589 __description("unpriv: write pointer into map elem value") 590 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 591 __retval(0) 592 __naked void pointer_into_map_elem_value(void) 593 { 594 asm volatile (" \ 595 r1 = 0; \ 596 *(u64*)(r10 - 8) = r1; \ 597 r2 = r10; \ 598 r2 += -8; \ 599 r1 = %[map_hash_8b] ll; \ 600 call %[bpf_map_lookup_elem]; \ 601 if r0 == 0 goto l0_%=; \ 602 *(u64*)(r0 + 0) = r0; \ 603 l0_%=: exit; \ 604 " : 605 : __imm(bpf_map_lookup_elem), 606 __imm_addr(map_hash_8b) 607 : __clobber_all); 608 } 609 610 SEC("socket") 611 __description("alu32: mov u32 const") 612 __success __success_unpriv 613 __retval(0) 614 #ifdef SPEC_V1 615 __xlated_unpriv("if r0 == 0x0 goto pc+2") 616 __xlated_unpriv("nospec") /* inserted to prevent `R7 invalid mem access 'scalar'` */ 617 __xlated_unpriv("goto pc-1") /* sanitized dead code */ 618 __xlated_unpriv("exit") 619 #endif 620 __naked void alu32_mov_u32_const(void) 621 { 622 asm volatile (" \ 623 w7 = 0; \ 624 w7 ^= w7; \ 625 w0 = w7; \ 626 if r0 == 0 goto l0_%=; \ 627 r0 = *(u64*)(r7 + 0); \ 628 l0_%=: exit; \ 629 " ::: __clobber_all); 630 } 631 632 SEC("socket") 633 __description("unpriv: partial copy of pointer") 634 __success __failure_unpriv __msg_unpriv("R10 partial copy") 635 __retval(0) 636 __naked void unpriv_partial_copy_of_pointer(void) 637 { 638 asm volatile (" \ 639 w1 = w10; \ 640 r0 = 0; \ 641 exit; \ 642 " ::: __clobber_all); 643 } 644 645 SEC("socket") 646 __description("unpriv: pass pointer to tail_call") 647 __success __failure_unpriv __msg_unpriv("R3 leaks addr into helper") 648 __retval(0) 649 __naked void pass_pointer_to_tail_call(void) 650 { 651 asm volatile (" \ 652 r3 = r1; \ 653 r2 = %[map_prog1_socket] ll; \ 654 call %[bpf_tail_call]; \ 655 r0 = 0; \ 656 exit; \ 657 " : 658 : __imm(bpf_tail_call), 659 __imm_addr(map_prog1_socket) 660 : __clobber_all); 661 } 662 663 SEC("socket") 664 __description("unpriv: cmp map pointer with zero") 665 __success __success_unpriv 666 __retval(0) 667 __naked void cmp_map_pointer_with_zero(void) 668 { 669 asm volatile (" \ 670 r1 = %[map_hash_8b] ll; \ 671 if r1 == 0 goto l0_%=; \ 672 l0_%=: r0 = 0; \ 673 exit; \ 674 " : 675 : __imm_addr(map_hash_8b) 676 : __clobber_all); 677 } 678 679 SEC("socket") 680 __description("unpriv: cmp map pointer with const") 681 __success __failure_unpriv __msg_unpriv("R1 pointer comparison prohibited") 682 __retval(0) 683 __naked void cmp_map_pointer_with_const(void) 684 { 685 asm volatile (" \ 686 r1 = %[map_hash_8b] ll; \ 687 if r1 == 0x0000beef goto l0_%=; \ 688 l0_%=: r0 = 0; \ 689 exit; \ 690 " : 691 : __imm_addr(map_hash_8b) 692 : __clobber_all); 693 } 694 695 SEC("socket") 696 __description("unpriv: write into frame pointer") 697 __failure __msg("frame pointer is read only") 698 __failure_unpriv 699 __naked void unpriv_write_into_frame_pointer(void) 700 { 701 asm volatile (" \ 702 r10 = r1; \ 703 r0 = 0; \ 704 exit; \ 705 " ::: __clobber_all); 706 } 707 708 SEC("socket") 709 __description("unpriv: spill/fill frame pointer") 710 __failure __msg("frame pointer is read only") 711 __failure_unpriv 712 __naked void unpriv_spill_fill_frame_pointer(void) 713 { 714 asm volatile (" \ 715 r6 = r10; \ 716 r6 += -8; \ 717 *(u64*)(r6 + 0) = r10; \ 718 r10 = *(u64*)(r6 + 0); \ 719 r0 = 0; \ 720 exit; \ 721 " ::: __clobber_all); 722 } 723 724 SEC("socket") 725 __description("unpriv: cmp of frame pointer") 726 __success __failure_unpriv __msg_unpriv("R10 pointer comparison") 727 __retval(0) 728 __naked void unpriv_cmp_of_frame_pointer(void) 729 { 730 asm volatile (" \ 731 if r10 == 0 goto l0_%=; \ 732 l0_%=: r0 = 0; \ 733 exit; \ 734 " ::: __clobber_all); 735 } 736 737 SEC("socket") 738 __description("unpriv: adding of fp, reg") 739 __success __failure_unpriv 740 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 741 __retval(0) 742 __naked void unpriv_adding_of_fp_reg(void) 743 { 744 asm volatile (" \ 745 r0 = 0; \ 746 r1 = 0; \ 747 r1 += r10; \ 748 *(u64*)(r1 - 8) = r0; \ 749 exit; \ 750 " ::: __clobber_all); 751 } 752 753 SEC("socket") 754 __description("unpriv: adding of fp, imm") 755 __success __failure_unpriv 756 __msg_unpriv("R1 stack pointer arithmetic goes out of range") 757 __retval(0) 758 __naked void unpriv_adding_of_fp_imm(void) 759 { 760 asm volatile (" \ 761 r0 = 0; \ 762 r1 = r10; \ 763 r1 += 0; \ 764 *(u64*)(r1 - 8) = r0; \ 765 exit; \ 766 " ::: __clobber_all); 767 } 768 769 SEC("socket") 770 __description("unpriv: cmp of stack pointer") 771 __success __failure_unpriv __msg_unpriv("R2 pointer comparison") 772 __retval(0) 773 __naked void unpriv_cmp_of_stack_pointer(void) 774 { 775 asm volatile (" \ 776 r2 = r10; \ 777 r2 += -8; \ 778 if r2 == 0 goto l0_%=; \ 779 l0_%=: r0 = 0; \ 780 exit; \ 781 " ::: __clobber_all); 782 } 783 784 SEC("socket") 785 __description("unpriv: Spectre v1 path-based type confusion of scalar as stack-ptr") 786 __success __success_unpriv __retval(0) 787 #ifdef SPEC_V1 788 __xlated_unpriv("if r0 != 0x1 goto pc+2") 789 /* This nospec prevents the exploit because it forces the mispredicted (not 790 * taken) `if r0 != 0x0 goto l0_%=` to resolve before using r6 as a pointer. 791 * This causes the CPU to realize that `r6 = r9` should have never executed. It 792 * ensures that r6 always contains a readable stack slot ptr when the insn after 793 * the nospec executes. 794 */ 795 __xlated_unpriv("nospec") 796 __xlated_unpriv("r9 = *(u8 *)(r6 +0)") 797 #endif 798 __naked void unpriv_spec_v1_type_confusion(void) 799 { 800 asm volatile (" \ 801 r1 = 0; \ 802 *(u64*)(r10 - 8) = r1; \ 803 r2 = r10; \ 804 r2 += -8; \ 805 r1 = %[map_hash_8b] ll; \ 806 call %[bpf_map_lookup_elem]; \ 807 if r0 == 0 goto l2_%=; \ 808 /* r0: pointer to a map array entry */ \ 809 r2 = r10; \ 810 r2 += -8; \ 811 r1 = %[map_hash_8b] ll; \ 812 /* r1, r2: prepared call args */ \ 813 r6 = r10; \ 814 r6 += -8; \ 815 /* r6: pointer to readable stack slot */ \ 816 r9 = 0xffffc900; \ 817 r9 <<= 32; \ 818 /* r9: scalar controlled by attacker */ \ 819 r0 = *(u64 *)(r0 + 0); /* cache miss */ \ 820 if r0 != 0x0 goto l0_%=; \ 821 r6 = r9; \ 822 l0_%=: if r0 != 0x1 goto l1_%=; \ 823 r9 = *(u8 *)(r6 + 0); \ 824 l1_%=: /* leak r9 */ \ 825 r9 &= 1; \ 826 r9 <<= 9; \ 827 *(u64*)(r10 - 8) = r9; \ 828 call %[bpf_map_lookup_elem]; \ 829 if r0 == 0 goto l2_%=; \ 830 /* leak secret into is_cached(map[0|512]): */ \ 831 r0 = *(u64 *)(r0 + 0); \ 832 l2_%=: \ 833 r0 = 0; \ 834 exit; \ 835 " : 836 : __imm(bpf_map_lookup_elem), 837 __imm_addr(map_hash_8b) 838 : __clobber_all); 839 } 840 841 SEC("socket") 842 __description("unpriv: ldimm64 before Spectre v4 barrier") 843 __success __success_unpriv 844 __retval(0) 845 #ifdef SPEC_V4 846 __xlated_unpriv("r1 = 0x2020200005642020") /* should not matter */ 847 __xlated_unpriv("*(u64 *)(r10 -8) = r1") 848 __xlated_unpriv("nospec") 849 #endif 850 __naked void unpriv_ldimm64_spectre_v4(void) 851 { 852 asm volatile (" \ 853 r1 = 0x2020200005642020 ll; \ 854 *(u64 *)(r10 -8) = r1; \ 855 r0 = 0; \ 856 exit; \ 857 " ::: __clobber_all); 858 } 859 860 SEC("socket") 861 __description("unpriv: Spectre v1 and v4 barrier") 862 __success __success_unpriv 863 __retval(0) 864 #ifdef SPEC_V1 865 #ifdef SPEC_V4 866 /* starts with r0 == r8 == r9 == 0 */ 867 __xlated_unpriv("if r8 != 0x0 goto pc+1") 868 __xlated_unpriv("goto pc+2") 869 __xlated_unpriv("if r9 == 0x0 goto pc+4") 870 __xlated_unpriv("r2 = r0") 871 /* Following nospec required to prevent following dangerous `*(u64 *)(NOT_FP -64) 872 * = r1` iff `if r9 == 0 goto pc+4` was mispredicted because of Spectre v1. The 873 * test therefore ensures the Spectre-v4--induced nospec does not prevent the 874 * Spectre-v1--induced speculative path from being fully analyzed. 875 */ 876 __xlated_unpriv("nospec") /* Spectre v1 */ 877 __xlated_unpriv("*(u64 *)(r2 -64) = r1") /* could be used to leak r2 */ 878 __xlated_unpriv("nospec") /* Spectre v4 */ 879 #endif 880 #endif 881 __naked void unpriv_spectre_v1_and_v4(void) 882 { 883 asm volatile (" \ 884 r1 = 0; \ 885 *(u64*)(r10 - 8) = r1; \ 886 r2 = r10; \ 887 r2 += -8; \ 888 r1 = %[map_hash_8b] ll; \ 889 call %[bpf_map_lookup_elem]; \ 890 r8 = r0; \ 891 r2 = r10; \ 892 r2 += -8; \ 893 r1 = %[map_hash_8b] ll; \ 894 call %[bpf_map_lookup_elem]; \ 895 r9 = r0; \ 896 r0 = r10; \ 897 r1 = 0; \ 898 r2 = r10; \ 899 if r8 != 0 goto l0_%=; \ 900 if r9 != 0 goto l0_%=; \ 901 r0 = 0; \ 902 l0_%=: if r8 != 0 goto l1_%=; \ 903 goto l2_%=; \ 904 l1_%=: if r9 == 0 goto l3_%=; \ 905 r2 = r0; \ 906 l2_%=: *(u64 *)(r2 -64) = r1; \ 907 l3_%=: r0 = 0; \ 908 exit; \ 909 " : 910 : __imm(bpf_map_lookup_elem), 911 __imm_addr(map_hash_8b) 912 : __clobber_all); 913 } 914 915 SEC("socket") 916 __description("unpriv: Spectre v1 and v4 barrier (simple)") 917 __success __success_unpriv 918 __retval(0) 919 #ifdef SPEC_V1 920 #ifdef SPEC_V4 921 __xlated_unpriv("if r8 != 0x0 goto pc+1") 922 __xlated_unpriv("goto pc+2") 923 __xlated_unpriv("goto pc-1") /* if r9 == 0 goto l3_%= */ 924 __xlated_unpriv("goto pc-1") /* r2 = r0 */ 925 __xlated_unpriv("nospec") 926 __xlated_unpriv("*(u64 *)(r2 -64) = r1") 927 __xlated_unpriv("nospec") 928 #endif 929 #endif 930 __naked void unpriv_spectre_v1_and_v4_simple(void) 931 { 932 asm volatile (" \ 933 r8 = 0; \ 934 r8 ^= r8; \ 935 r9 = 0; \ 936 r9 ^= r9; \ 937 r0 = r10; \ 938 r1 = 0; \ 939 r2 = r10; \ 940 if r8 != 0 goto l0_%=; \ 941 if r9 != 0 goto l0_%=; \ 942 r0 = 0; \ 943 l0_%=: if r8 != 0 goto l1_%=; \ 944 goto l2_%=; \ 945 l1_%=: if r9 == 0 goto l3_%=; \ 946 r2 = r0; \ 947 l2_%=: *(u64 *)(r2 -64) = r1; \ 948 l3_%=: r0 = 0; \ 949 exit; \ 950 " ::: __clobber_all); 951 } 952 953 SEC("socket") 954 __description("unpriv: ldimm64 before Spectre v1 and v4 barrier (simple)") 955 __success __success_unpriv 956 __retval(0) 957 #ifdef SPEC_V1 958 #ifdef SPEC_V4 959 __xlated_unpriv("if r8 != 0x0 goto pc+1") 960 __xlated_unpriv("goto pc+4") 961 __xlated_unpriv("goto pc-1") /* if r9 == 0 goto l3_%= */ 962 __xlated_unpriv("goto pc-1") /* r2 = r0 */ 963 __xlated_unpriv("goto pc-1") /* r1 = 0x2020200005642020 ll */ 964 __xlated_unpriv("goto pc-1") /* second part of ldimm64 */ 965 __xlated_unpriv("nospec") 966 __xlated_unpriv("*(u64 *)(r2 -64) = r1") 967 __xlated_unpriv("nospec") 968 #endif 969 #endif 970 __naked void unpriv_ldimm64_spectre_v1_and_v4_simple(void) 971 { 972 asm volatile (" \ 973 r8 = 0; \ 974 r8 ^= r8; \ 975 r9 = 0; \ 976 r9 ^= r9; \ 977 r0 = r10; \ 978 r1 = 0; \ 979 r2 = r10; \ 980 if r8 != 0 goto l0_%=; \ 981 if r9 != 0 goto l0_%=; \ 982 r0 = 0; \ 983 l0_%=: if r8 != 0 goto l1_%=; \ 984 goto l2_%=; \ 985 l1_%=: if r9 == 0 goto l3_%=; \ 986 r2 = r0; \ 987 r1 = 0x2020200005642020 ll; \ 988 l2_%=: *(u64 *)(r2 -64) = r1; \ 989 l3_%=: r0 = 0; \ 990 exit; \ 991 " ::: __clobber_all); 992 } 993 994 SEC("socket") 995 __description("unpriv: nospec after dead stack write in helper") 996 __success __success_unpriv 997 __retval(0) 998 /* Dead code sanitizer rewrites the call to `goto -1`. */ 999 __naked void unpriv_dead_helper_stack_write_nospec_result(void) 1000 { 1001 asm volatile (" \ 1002 r0 = 0; \ 1003 if r0 != 1 goto l0_%=; \ 1004 r2 = 0; \ 1005 r3 = r10; \ 1006 r3 += -16; \ 1007 r4 = 4; \ 1008 r5 = 0; \ 1009 call %[bpf_skb_load_bytes_relative]; \ 1010 l0_%=: exit; \ 1011 " : 1012 : __imm(bpf_skb_load_bytes_relative) 1013 : __clobber_all); 1014 } 1015 1016 SEC("socket") 1017 __description("unpriv: Spectre v4 stack write slot index") 1018 __success __success_unpriv 1019 __retval(0) 1020 #ifdef SPEC_V4 1021 __xlated_unpriv("r0 = 0") 1022 __xlated_unpriv("*(u32 *)(r10 -4) = r0") 1023 __xlated_unpriv("nospec") 1024 __xlated_unpriv("*(u32 *)(r10 -8) = r0") 1025 __xlated_unpriv("nospec") 1026 __xlated_unpriv("exit") 1027 #endif 1028 __naked void stack_write_nospec_slot_index(void) 1029 { 1030 asm volatile (" \ 1031 r0 = 0; \ 1032 *(u32 *)(r10 - 4) = r0; \ 1033 *(u32 *)(r10 - 8) = r0; \ 1034 exit; \ 1035 " ::: __clobber_all); 1036 } 1037 1038 __noinline int nospec_global_func(int x) 1039 { 1040 return x + 1; 1041 } 1042 1043 __used __naked static void nospec_call_subprog(void) 1044 { 1045 asm volatile (" \ 1046 call %[bpf_get_prandom_u32]; \ 1047 r0 &= 0xff; \ 1048 r1 = 0; \ 1049 if r0 < 0x100 goto l0_%=; \ 1050 /* executed only speculatively */ \ 1051 r1 = r10; \ 1052 l0_%=: call nospec_global_func; \ 1053 exit; \ 1054 " : 1055 : __imm(bpf_get_prandom_u32) 1056 : __clobber_all); 1057 } 1058 1059 /* nospec_global_func is placed in front of nospec_call_subprog */ 1060 SEC("socket") 1061 __description("unpriv: nospec in front of a call") 1062 __success __success_unpriv 1063 __caps_unpriv(CAP_BPF) 1064 __retval(1) 1065 __naked void nospec_backward_call(void) 1066 { 1067 asm volatile (" \ 1068 r1 = 0; \ 1069 call nospec_global_func; \ 1070 call nospec_call_subprog; \ 1071 exit; \ 1072 " ::: __clobber_all); 1073 } 1074 1075 char _license[] SEC("license") = "GPL"; 1076