1 // SPDX-License-Identifier: GPL-2.0 2 /* Converted from tools/testing/selftests/bpf/verifier/bounds.c */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include "bpf_misc.h" 7 8 struct { 9 __uint(type, BPF_MAP_TYPE_HASH); 10 __uint(max_entries, 1); 11 __type(key, long long); 12 __type(value, long long); 13 } map_hash_8b SEC(".maps"); 14 15 SEC("socket") 16 __description("subtraction bounds (map value) variant 1") 17 __failure __msg("R0 max value is outside of the allowed memory range") 18 __failure_unpriv 19 __naked void bounds_map_value_variant_1(void) 20 { 21 asm volatile (" \ 22 r1 = 0; \ 23 *(u64*)(r10 - 8) = r1; \ 24 r2 = r10; \ 25 r2 += -8; \ 26 r1 = %[map_hash_8b] ll; \ 27 call %[bpf_map_lookup_elem]; \ 28 if r0 == 0 goto l0_%=; \ 29 r1 = *(u8*)(r0 + 0); \ 30 if r1 > 0xff goto l0_%=; \ 31 r3 = *(u8*)(r0 + 1); \ 32 if r3 > 0xff goto l0_%=; \ 33 r1 -= r3; \ 34 r1 >>= 56; \ 35 r0 += r1; \ 36 r0 = *(u8*)(r0 + 0); \ 37 exit; \ 38 l0_%=: r0 = 0; \ 39 exit; \ 40 " : 41 : __imm(bpf_map_lookup_elem), 42 __imm_addr(map_hash_8b) 43 : __clobber_all); 44 } 45 46 SEC("socket") 47 __description("subtraction bounds (map value) variant 2") 48 __failure 49 __msg("R0 min value is negative, either use unsigned index or do a if (index >=0) check.") 50 __msg_unpriv("R1 has unknown scalar with mixed signed bounds") 51 __naked void bounds_map_value_variant_2(void) 52 { 53 asm volatile (" \ 54 r1 = 0; \ 55 *(u64*)(r10 - 8) = r1; \ 56 r2 = r10; \ 57 r2 += -8; \ 58 r1 = %[map_hash_8b] ll; \ 59 call %[bpf_map_lookup_elem]; \ 60 if r0 == 0 goto l0_%=; \ 61 r1 = *(u8*)(r0 + 0); \ 62 if r1 > 0xff goto l0_%=; \ 63 r3 = *(u8*)(r0 + 1); \ 64 if r3 > 0xff goto l0_%=; \ 65 r1 -= r3; \ 66 r0 += r1; \ 67 r0 = *(u8*)(r0 + 0); \ 68 exit; \ 69 l0_%=: r0 = 0; \ 70 exit; \ 71 " : 72 : __imm(bpf_map_lookup_elem), 73 __imm_addr(map_hash_8b) 74 : __clobber_all); 75 } 76 77 SEC("socket") 78 __description("check subtraction on pointers for unpriv") 79 __success __failure_unpriv __msg_unpriv("R9 pointer -= pointer prohibited") 80 __retval(0) 81 __naked void subtraction_on_pointers_for_unpriv(void) 82 { 83 asm volatile (" \ 84 r0 = 0; \ 85 r1 = %[map_hash_8b] ll; \ 86 r2 = r10; \ 87 r2 += -8; \ 88 r6 = 9; \ 89 *(u64*)(r2 + 0) = r6; \ 90 call %[bpf_map_lookup_elem]; \ 91 r9 = r10; \ 92 r9 -= r0; \ 93 r1 = %[map_hash_8b] ll; \ 94 r2 = r10; \ 95 r2 += -8; \ 96 r6 = 0; \ 97 *(u64*)(r2 + 0) = r6; \ 98 call %[bpf_map_lookup_elem]; \ 99 if r0 != 0 goto l0_%=; \ 100 exit; \ 101 l0_%=: *(u64*)(r0 + 0) = r9; \ 102 r0 = 0; \ 103 exit; \ 104 " : 105 : __imm(bpf_map_lookup_elem), 106 __imm_addr(map_hash_8b) 107 : __clobber_all); 108 } 109 110 SEC("socket") 111 __description("bounds check based on zero-extended MOV") 112 __success __success_unpriv __retval(0) 113 __naked void based_on_zero_extended_mov(void) 114 { 115 asm volatile (" \ 116 r1 = 0; \ 117 *(u64*)(r10 - 8) = r1; \ 118 r2 = r10; \ 119 r2 += -8; \ 120 r1 = %[map_hash_8b] ll; \ 121 call %[bpf_map_lookup_elem]; \ 122 if r0 == 0 goto l0_%=; \ 123 /* r2 = 0x0000'0000'ffff'ffff */ \ 124 w2 = 0xffffffff; \ 125 /* r2 = 0 */ \ 126 r2 >>= 32; \ 127 /* no-op */ \ 128 r0 += r2; \ 129 /* access at offset 0 */ \ 130 r0 = *(u8*)(r0 + 0); \ 131 l0_%=: /* exit */ \ 132 r0 = 0; \ 133 exit; \ 134 " : 135 : __imm(bpf_map_lookup_elem), 136 __imm_addr(map_hash_8b) 137 : __clobber_all); 138 } 139 140 SEC("socket") 141 __description("bounds check based on sign-extended MOV. test1") 142 __failure __msg("map_value pointer and 4294967295") 143 __failure_unpriv 144 __naked void on_sign_extended_mov_test1(void) 145 { 146 asm volatile (" \ 147 r1 = 0; \ 148 *(u64*)(r10 - 8) = r1; \ 149 r2 = r10; \ 150 r2 += -8; \ 151 r1 = %[map_hash_8b] ll; \ 152 call %[bpf_map_lookup_elem]; \ 153 if r0 == 0 goto l0_%=; \ 154 /* r2 = 0xffff'ffff'ffff'ffff */ \ 155 r2 = 0xffffffff; \ 156 /* r2 = 0xffff'ffff */ \ 157 r2 >>= 32; \ 158 /* r0 = <oob pointer> */ \ 159 r0 += r2; \ 160 /* access to OOB pointer */ \ 161 r0 = *(u8*)(r0 + 0); \ 162 l0_%=: /* exit */ \ 163 r0 = 0; \ 164 exit; \ 165 " : 166 : __imm(bpf_map_lookup_elem), 167 __imm_addr(map_hash_8b) 168 : __clobber_all); 169 } 170 171 SEC("socket") 172 __description("bounds check based on sign-extended MOV. test2") 173 __failure __msg("R0 min value is outside of the allowed memory range") 174 __failure_unpriv 175 __naked void on_sign_extended_mov_test2(void) 176 { 177 asm volatile (" \ 178 r1 = 0; \ 179 *(u64*)(r10 - 8) = r1; \ 180 r2 = r10; \ 181 r2 += -8; \ 182 r1 = %[map_hash_8b] ll; \ 183 call %[bpf_map_lookup_elem]; \ 184 if r0 == 0 goto l0_%=; \ 185 /* r2 = 0xffff'ffff'ffff'ffff */ \ 186 r2 = 0xffffffff; \ 187 /* r2 = 0xfff'ffff */ \ 188 r2 >>= 36; \ 189 /* r0 = <oob pointer> */ \ 190 r0 += r2; \ 191 /* access to OOB pointer */ \ 192 r0 = *(u8*)(r0 + 0); \ 193 l0_%=: /* exit */ \ 194 r0 = 0; \ 195 exit; \ 196 " : 197 : __imm(bpf_map_lookup_elem), 198 __imm_addr(map_hash_8b) 199 : __clobber_all); 200 } 201 202 SEC("tc") 203 __description("bounds check based on reg_off + var_off + insn_off. test1") 204 __failure __msg("value_size=8 off=1073741825") 205 __naked void var_off_insn_off_test1(void) 206 { 207 asm volatile (" \ 208 r6 = *(u32*)(r1 + %[__sk_buff_mark]); \ 209 r1 = 0; \ 210 *(u64*)(r10 - 8) = r1; \ 211 r2 = r10; \ 212 r2 += -8; \ 213 r1 = %[map_hash_8b] ll; \ 214 call %[bpf_map_lookup_elem]; \ 215 if r0 == 0 goto l0_%=; \ 216 r6 &= 1; \ 217 r6 += %[__imm_0]; \ 218 r0 += r6; \ 219 r0 += %[__imm_0]; \ 220 l0_%=: r0 = *(u8*)(r0 + 3); \ 221 r0 = 0; \ 222 exit; \ 223 " : 224 : __imm(bpf_map_lookup_elem), 225 __imm_addr(map_hash_8b), 226 __imm_const(__imm_0, (1 << 29) - 1), 227 __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)) 228 : __clobber_all); 229 } 230 231 SEC("tc") 232 __description("bounds check based on reg_off + var_off + insn_off. test2") 233 __failure __msg("value 1073741823") 234 __naked void var_off_insn_off_test2(void) 235 { 236 asm volatile (" \ 237 r6 = *(u32*)(r1 + %[__sk_buff_mark]); \ 238 r1 = 0; \ 239 *(u64*)(r10 - 8) = r1; \ 240 r2 = r10; \ 241 r2 += -8; \ 242 r1 = %[map_hash_8b] ll; \ 243 call %[bpf_map_lookup_elem]; \ 244 if r0 == 0 goto l0_%=; \ 245 r6 &= 1; \ 246 r6 += %[__imm_0]; \ 247 r0 += r6; \ 248 r0 += %[__imm_1]; \ 249 l0_%=: r0 = *(u8*)(r0 + 3); \ 250 r0 = 0; \ 251 exit; \ 252 " : 253 : __imm(bpf_map_lookup_elem), 254 __imm_addr(map_hash_8b), 255 __imm_const(__imm_0, (1 << 30) - 1), 256 __imm_const(__imm_1, (1 << 29) - 1), 257 __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)) 258 : __clobber_all); 259 } 260 261 SEC("socket") 262 __description("bounds check after truncation of non-boundary-crossing range") 263 __success __success_unpriv __retval(0) 264 __naked void of_non_boundary_crossing_range(void) 265 { 266 asm volatile (" \ 267 r1 = 0; \ 268 *(u64*)(r10 - 8) = r1; \ 269 r2 = r10; \ 270 r2 += -8; \ 271 r1 = %[map_hash_8b] ll; \ 272 call %[bpf_map_lookup_elem]; \ 273 if r0 == 0 goto l0_%=; \ 274 /* r1 = [0x00, 0xff] */ \ 275 r1 = *(u8*)(r0 + 0); \ 276 r2 = 1; \ 277 /* r2 = 0x10'0000'0000 */ \ 278 r2 <<= 36; \ 279 /* r1 = [0x10'0000'0000, 0x10'0000'00ff] */ \ 280 r1 += r2; \ 281 /* r1 = [0x10'7fff'ffff, 0x10'8000'00fe] */ \ 282 r1 += 0x7fffffff; \ 283 /* r1 = [0x00, 0xff] */ \ 284 w1 -= 0x7fffffff; \ 285 /* r1 = 0 */ \ 286 r1 >>= 8; \ 287 /* no-op */ \ 288 r0 += r1; \ 289 /* access at offset 0 */ \ 290 r0 = *(u8*)(r0 + 0); \ 291 l0_%=: /* exit */ \ 292 r0 = 0; \ 293 exit; \ 294 " : 295 : __imm(bpf_map_lookup_elem), 296 __imm_addr(map_hash_8b) 297 : __clobber_all); 298 } 299 300 SEC("socket") 301 __description("bounds check after truncation of boundary-crossing range (1)") 302 __failure 303 /* not actually fully unbounded, but the bound is very high */ 304 __msg("value -4294967168 makes map_value pointer be out of bounds") 305 __failure_unpriv 306 __naked void of_boundary_crossing_range_1(void) 307 { 308 asm volatile (" \ 309 r1 = 0; \ 310 *(u64*)(r10 - 8) = r1; \ 311 r2 = r10; \ 312 r2 += -8; \ 313 r1 = %[map_hash_8b] ll; \ 314 call %[bpf_map_lookup_elem]; \ 315 if r0 == 0 goto l0_%=; \ 316 /* r1 = [0x00, 0xff] */ \ 317 r1 = *(u8*)(r0 + 0); \ 318 r1 += %[__imm_0]; \ 319 /* r1 = [0xffff'ff80, 0x1'0000'007f] */ \ 320 r1 += %[__imm_0]; \ 321 /* r1 = [0xffff'ff80, 0xffff'ffff] or \ 322 * [0x0000'0000, 0x0000'007f] \ 323 */ \ 324 w1 += 0; \ 325 r1 -= %[__imm_0]; \ 326 /* r1 = [0x00, 0xff] or \ 327 * [0xffff'ffff'0000'0080, 0xffff'ffff'ffff'ffff]\ 328 */ \ 329 r1 -= %[__imm_0]; \ 330 /* error on OOB pointer computation */ \ 331 r0 += r1; \ 332 /* exit */ \ 333 r0 = 0; \ 334 l0_%=: exit; \ 335 " : 336 : __imm(bpf_map_lookup_elem), 337 __imm_addr(map_hash_8b), 338 __imm_const(__imm_0, 0xffffff80 >> 1) 339 : __clobber_all); 340 } 341 342 SEC("socket") 343 __description("bounds check after truncation of boundary-crossing range (2)") 344 __failure __msg("value -4294967168 makes map_value pointer be out of bounds") 345 __failure_unpriv 346 __naked void of_boundary_crossing_range_2(void) 347 { 348 asm volatile (" \ 349 r1 = 0; \ 350 *(u64*)(r10 - 8) = r1; \ 351 r2 = r10; \ 352 r2 += -8; \ 353 r1 = %[map_hash_8b] ll; \ 354 call %[bpf_map_lookup_elem]; \ 355 if r0 == 0 goto l0_%=; \ 356 /* r1 = [0x00, 0xff] */ \ 357 r1 = *(u8*)(r0 + 0); \ 358 r1 += %[__imm_0]; \ 359 /* r1 = [0xffff'ff80, 0x1'0000'007f] */ \ 360 r1 += %[__imm_0]; \ 361 /* r1 = [0xffff'ff80, 0xffff'ffff] or \ 362 * [0x0000'0000, 0x0000'007f] \ 363 * difference to previous test: truncation via MOV32\ 364 * instead of ALU32. \ 365 */ \ 366 w1 = w1; \ 367 r1 -= %[__imm_0]; \ 368 /* r1 = [0x00, 0xff] or \ 369 * [0xffff'ffff'0000'0080, 0xffff'ffff'ffff'ffff]\ 370 */ \ 371 r1 -= %[__imm_0]; \ 372 /* error on OOB pointer computation */ \ 373 r0 += r1; \ 374 /* exit */ \ 375 r0 = 0; \ 376 l0_%=: exit; \ 377 " : 378 : __imm(bpf_map_lookup_elem), 379 __imm_addr(map_hash_8b), 380 __imm_const(__imm_0, 0xffffff80 >> 1) 381 : __clobber_all); 382 } 383 384 SEC("socket") 385 __description("bounds check after wrapping 32-bit addition") 386 __success __success_unpriv __retval(0) 387 __naked void after_wrapping_32_bit_addition(void) 388 { 389 asm volatile (" \ 390 r1 = 0; \ 391 *(u64*)(r10 - 8) = r1; \ 392 r2 = r10; \ 393 r2 += -8; \ 394 r1 = %[map_hash_8b] ll; \ 395 call %[bpf_map_lookup_elem]; \ 396 if r0 == 0 goto l0_%=; \ 397 /* r1 = 0x7fff'ffff */ \ 398 r1 = 0x7fffffff; \ 399 /* r1 = 0xffff'fffe */ \ 400 r1 += 0x7fffffff; \ 401 /* r1 = 0 */ \ 402 w1 += 2; \ 403 /* no-op */ \ 404 r0 += r1; \ 405 /* access at offset 0 */ \ 406 r0 = *(u8*)(r0 + 0); \ 407 l0_%=: /* exit */ \ 408 r0 = 0; \ 409 exit; \ 410 " : 411 : __imm(bpf_map_lookup_elem), 412 __imm_addr(map_hash_8b) 413 : __clobber_all); 414 } 415 416 SEC("socket") 417 __description("bounds check after shift with oversized count operand") 418 __failure __msg("R0 max value is outside of the allowed memory range") 419 __failure_unpriv 420 __naked void shift_with_oversized_count_operand(void) 421 { 422 asm volatile (" \ 423 r1 = 0; \ 424 *(u64*)(r10 - 8) = r1; \ 425 r2 = r10; \ 426 r2 += -8; \ 427 r1 = %[map_hash_8b] ll; \ 428 call %[bpf_map_lookup_elem]; \ 429 if r0 == 0 goto l0_%=; \ 430 r2 = 32; \ 431 r1 = 1; \ 432 /* r1 = (u32)1 << (u32)32 = ? */ \ 433 w1 <<= w2; \ 434 /* r1 = [0x0000, 0xffff] */ \ 435 r1 &= 0xffff; \ 436 /* computes unknown pointer, potentially OOB */ \ 437 r0 += r1; \ 438 /* potentially OOB access */ \ 439 r0 = *(u8*)(r0 + 0); \ 440 l0_%=: /* exit */ \ 441 r0 = 0; \ 442 exit; \ 443 " : 444 : __imm(bpf_map_lookup_elem), 445 __imm_addr(map_hash_8b) 446 : __clobber_all); 447 } 448 449 SEC("socket") 450 __description("bounds check after right shift of maybe-negative number") 451 __failure __msg("R0 unbounded memory access") 452 __failure_unpriv 453 __naked void shift_of_maybe_negative_number(void) 454 { 455 asm volatile (" \ 456 r1 = 0; \ 457 *(u64*)(r10 - 8) = r1; \ 458 r2 = r10; \ 459 r2 += -8; \ 460 r1 = %[map_hash_8b] ll; \ 461 call %[bpf_map_lookup_elem]; \ 462 if r0 == 0 goto l0_%=; \ 463 /* r1 = [0x00, 0xff] */ \ 464 r1 = *(u8*)(r0 + 0); \ 465 /* r1 = [-0x01, 0xfe] */ \ 466 r1 -= 1; \ 467 /* r1 = 0 or 0xff'ffff'ffff'ffff */ \ 468 r1 >>= 8; \ 469 /* r1 = 0 or 0xffff'ffff'ffff */ \ 470 r1 >>= 8; \ 471 /* computes unknown pointer, potentially OOB */ \ 472 r0 += r1; \ 473 /* potentially OOB access */ \ 474 r0 = *(u8*)(r0 + 0); \ 475 l0_%=: /* exit */ \ 476 r0 = 0; \ 477 exit; \ 478 " : 479 : __imm(bpf_map_lookup_elem), 480 __imm_addr(map_hash_8b) 481 : __clobber_all); 482 } 483 484 SEC("socket") 485 __description("bounds check after 32-bit right shift with 64-bit input") 486 __failure __msg("math between map_value pointer and 4294967294 is not allowed") 487 __failure_unpriv 488 __naked void shift_with_64_bit_input(void) 489 { 490 asm volatile (" \ 491 r1 = 0; \ 492 *(u64*)(r10 - 8) = r1; \ 493 r2 = r10; \ 494 r2 += -8; \ 495 r1 = %[map_hash_8b] ll; \ 496 call %[bpf_map_lookup_elem]; \ 497 if r0 == 0 goto l0_%=; \ 498 r1 = 2; \ 499 /* r1 = 1<<32 */ \ 500 r1 <<= 31; \ 501 /* r1 = 0 (NOT 2!) */ \ 502 w1 >>= 31; \ 503 /* r1 = 0xffff'fffe (NOT 0!) */ \ 504 w1 -= 2; \ 505 /* error on computing OOB pointer */ \ 506 r0 += r1; \ 507 /* exit */ \ 508 r0 = 0; \ 509 l0_%=: exit; \ 510 " : 511 : __imm(bpf_map_lookup_elem), 512 __imm_addr(map_hash_8b) 513 : __clobber_all); 514 } 515 516 SEC("socket") 517 __description("bounds check map access with off+size signed 32bit overflow. test1") 518 __failure __msg("map_value pointer and 2147483646") 519 __failure_unpriv 520 __naked void size_signed_32bit_overflow_test1(void) 521 { 522 asm volatile (" \ 523 r1 = 0; \ 524 *(u64*)(r10 - 8) = r1; \ 525 r2 = r10; \ 526 r2 += -8; \ 527 r1 = %[map_hash_8b] ll; \ 528 call %[bpf_map_lookup_elem]; \ 529 if r0 != 0 goto l0_%=; \ 530 exit; \ 531 l0_%=: r0 += 0x7ffffffe; \ 532 r0 = *(u64*)(r0 + 0); \ 533 goto l1_%=; \ 534 l1_%=: exit; \ 535 " : 536 : __imm(bpf_map_lookup_elem), 537 __imm_addr(map_hash_8b) 538 : __clobber_all); 539 } 540 541 SEC("socket") 542 __description("bounds check map access with off+size signed 32bit overflow. test2") 543 __failure __msg("pointer offset 1073741822") 544 __msg_unpriv("R0 pointer arithmetic of map value goes out of range") 545 __naked void size_signed_32bit_overflow_test2(void) 546 { 547 asm volatile (" \ 548 r1 = 0; \ 549 *(u64*)(r10 - 8) = r1; \ 550 r2 = r10; \ 551 r2 += -8; \ 552 r1 = %[map_hash_8b] ll; \ 553 call %[bpf_map_lookup_elem]; \ 554 if r0 != 0 goto l0_%=; \ 555 exit; \ 556 l0_%=: r0 += 0x1fffffff; \ 557 r0 += 0x1fffffff; \ 558 r0 += 0x1fffffff; \ 559 r0 = *(u64*)(r0 + 0); \ 560 goto l1_%=; \ 561 l1_%=: exit; \ 562 " : 563 : __imm(bpf_map_lookup_elem), 564 __imm_addr(map_hash_8b) 565 : __clobber_all); 566 } 567 568 SEC("socket") 569 __description("bounds check map access with off+size signed 32bit overflow. test3") 570 __failure __msg("pointer offset -1073741822") 571 __msg_unpriv("R0 pointer arithmetic of map value goes out of range") 572 __naked void size_signed_32bit_overflow_test3(void) 573 { 574 asm volatile (" \ 575 r1 = 0; \ 576 *(u64*)(r10 - 8) = r1; \ 577 r2 = r10; \ 578 r2 += -8; \ 579 r1 = %[map_hash_8b] ll; \ 580 call %[bpf_map_lookup_elem]; \ 581 if r0 != 0 goto l0_%=; \ 582 exit; \ 583 l0_%=: r0 -= 0x1fffffff; \ 584 r0 -= 0x1fffffff; \ 585 r0 = *(u64*)(r0 + 2); \ 586 goto l1_%=; \ 587 l1_%=: exit; \ 588 " : 589 : __imm(bpf_map_lookup_elem), 590 __imm_addr(map_hash_8b) 591 : __clobber_all); 592 } 593 594 SEC("socket") 595 __description("bounds check map access with off+size signed 32bit overflow. test4") 596 __failure __msg("map_value pointer and 1000000000000") 597 __failure_unpriv 598 __naked void size_signed_32bit_overflow_test4(void) 599 { 600 asm volatile (" \ 601 r1 = 0; \ 602 *(u64*)(r10 - 8) = r1; \ 603 r2 = r10; \ 604 r2 += -8; \ 605 r1 = %[map_hash_8b] ll; \ 606 call %[bpf_map_lookup_elem]; \ 607 if r0 != 0 goto l0_%=; \ 608 exit; \ 609 l0_%=: r1 = 1000000; \ 610 r1 *= 1000000; \ 611 r0 += r1; \ 612 r0 = *(u64*)(r0 + 2); \ 613 goto l1_%=; \ 614 l1_%=: exit; \ 615 " : 616 : __imm(bpf_map_lookup_elem), 617 __imm_addr(map_hash_8b) 618 : __clobber_all); 619 } 620 621 SEC("socket") 622 __description("bounds check mixed 32bit and 64bit arithmetic. test1") 623 __success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'") 624 __retval(0) 625 __naked void _32bit_and_64bit_arithmetic_test1(void) 626 { 627 asm volatile (" \ 628 r0 = 0; \ 629 r1 = -1; \ 630 r1 <<= 32; \ 631 r1 += 1; \ 632 /* r1 = 0xffffFFFF00000001 */ \ 633 if w1 > 1 goto l0_%=; \ 634 /* check ALU64 op keeps 32bit bounds */ \ 635 r1 += 1; \ 636 if w1 > 2 goto l0_%=; \ 637 goto l1_%=; \ 638 l0_%=: /* invalid ldx if bounds are lost above */ \ 639 r0 = *(u64*)(r0 - 1); \ 640 l1_%=: exit; \ 641 " ::: __clobber_all); 642 } 643 644 SEC("socket") 645 __description("bounds check mixed 32bit and 64bit arithmetic. test2") 646 __success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'") 647 __retval(0) 648 __naked void _32bit_and_64bit_arithmetic_test2(void) 649 { 650 asm volatile (" \ 651 r0 = 0; \ 652 r1 = -1; \ 653 r1 <<= 32; \ 654 r1 += 1; \ 655 /* r1 = 0xffffFFFF00000001 */ \ 656 r2 = 3; \ 657 /* r1 = 0x2 */ \ 658 w1 += 1; \ 659 /* check ALU32 op zero extends 64bit bounds */ \ 660 if r1 > r2 goto l0_%=; \ 661 goto l1_%=; \ 662 l0_%=: /* invalid ldx if bounds are lost above */ \ 663 r0 = *(u64*)(r0 - 1); \ 664 l1_%=: exit; \ 665 " ::: __clobber_all); 666 } 667 668 SEC("tc") 669 __description("assigning 32bit bounds to 64bit for wA = 0, wB = wA") 670 __success __retval(0) __flag(BPF_F_ANY_ALIGNMENT) 671 __naked void for_wa_0_wb_wa(void) 672 { 673 asm volatile (" \ 674 r8 = *(u32*)(r1 + %[__sk_buff_data_end]); \ 675 r7 = *(u32*)(r1 + %[__sk_buff_data]); \ 676 w9 = 0; \ 677 w2 = w9; \ 678 r6 = r7; \ 679 r6 += r2; \ 680 r3 = r6; \ 681 r3 += 8; \ 682 if r3 > r8 goto l0_%=; \ 683 r5 = *(u32*)(r6 + 0); \ 684 l0_%=: r0 = 0; \ 685 exit; \ 686 " : 687 : __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)), 688 __imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)) 689 : __clobber_all); 690 } 691 692 SEC("socket") 693 __description("bounds check for reg = 0, reg xor 1") 694 __success __failure_unpriv 695 __msg_unpriv("R0 min value is outside of the allowed memory range") 696 __retval(0) 697 __naked void reg_0_reg_xor_1(void) 698 { 699 asm volatile (" \ 700 r1 = 0; \ 701 *(u64*)(r10 - 8) = r1; \ 702 r2 = r10; \ 703 r2 += -8; \ 704 r1 = %[map_hash_8b] ll; \ 705 call %[bpf_map_lookup_elem]; \ 706 if r0 != 0 goto l0_%=; \ 707 exit; \ 708 l0_%=: r1 = 0; \ 709 r1 ^= 1; \ 710 if r1 != 0 goto l1_%=; \ 711 r0 = *(u64*)(r0 + 8); \ 712 l1_%=: r0 = 0; \ 713 exit; \ 714 " : 715 : __imm(bpf_map_lookup_elem), 716 __imm_addr(map_hash_8b) 717 : __clobber_all); 718 } 719 720 SEC("socket") 721 __description("bounds check for reg32 = 0, reg32 xor 1") 722 __success __failure_unpriv 723 __msg_unpriv("R0 min value is outside of the allowed memory range") 724 __retval(0) 725 __naked void reg32_0_reg32_xor_1(void) 726 { 727 asm volatile (" \ 728 r1 = 0; \ 729 *(u64*)(r10 - 8) = r1; \ 730 r2 = r10; \ 731 r2 += -8; \ 732 r1 = %[map_hash_8b] ll; \ 733 call %[bpf_map_lookup_elem]; \ 734 if r0 != 0 goto l0_%=; \ 735 exit; \ 736 l0_%=: w1 = 0; \ 737 w1 ^= 1; \ 738 if w1 != 0 goto l1_%=; \ 739 r0 = *(u64*)(r0 + 8); \ 740 l1_%=: r0 = 0; \ 741 exit; \ 742 " : 743 : __imm(bpf_map_lookup_elem), 744 __imm_addr(map_hash_8b) 745 : __clobber_all); 746 } 747 748 SEC("socket") 749 __description("bounds check for reg = 2, reg xor 3") 750 __success __failure_unpriv 751 __msg_unpriv("R0 min value is outside of the allowed memory range") 752 __retval(0) 753 __naked void reg_2_reg_xor_3(void) 754 { 755 asm volatile (" \ 756 r1 = 0; \ 757 *(u64*)(r10 - 8) = r1; \ 758 r2 = r10; \ 759 r2 += -8; \ 760 r1 = %[map_hash_8b] ll; \ 761 call %[bpf_map_lookup_elem]; \ 762 if r0 != 0 goto l0_%=; \ 763 exit; \ 764 l0_%=: r1 = 2; \ 765 r1 ^= 3; \ 766 if r1 > 0 goto l1_%=; \ 767 r0 = *(u64*)(r0 + 8); \ 768 l1_%=: r0 = 0; \ 769 exit; \ 770 " : 771 : __imm(bpf_map_lookup_elem), 772 __imm_addr(map_hash_8b) 773 : __clobber_all); 774 } 775 776 SEC("socket") 777 __description("bounds check for reg = any, reg xor 3") 778 __failure __msg("invalid access to map value") 779 __msg_unpriv("invalid access to map value") 780 __naked void reg_any_reg_xor_3(void) 781 { 782 asm volatile (" \ 783 r1 = 0; \ 784 *(u64*)(r10 - 8) = r1; \ 785 r2 = r10; \ 786 r2 += -8; \ 787 r1 = %[map_hash_8b] ll; \ 788 call %[bpf_map_lookup_elem]; \ 789 if r0 != 0 goto l0_%=; \ 790 exit; \ 791 l0_%=: r1 = *(u64*)(r0 + 0); \ 792 r1 ^= 3; \ 793 if r1 != 0 goto l1_%=; \ 794 r0 = *(u64*)(r0 + 8); \ 795 l1_%=: r0 = 0; \ 796 exit; \ 797 " : 798 : __imm(bpf_map_lookup_elem), 799 __imm_addr(map_hash_8b) 800 : __clobber_all); 801 } 802 803 SEC("socket") 804 __description("bounds check for reg32 = any, reg32 xor 3") 805 __failure __msg("invalid access to map value") 806 __msg_unpriv("invalid access to map value") 807 __naked void reg32_any_reg32_xor_3(void) 808 { 809 asm volatile (" \ 810 r1 = 0; \ 811 *(u64*)(r10 - 8) = r1; \ 812 r2 = r10; \ 813 r2 += -8; \ 814 r1 = %[map_hash_8b] ll; \ 815 call %[bpf_map_lookup_elem]; \ 816 if r0 != 0 goto l0_%=; \ 817 exit; \ 818 l0_%=: r1 = *(u64*)(r0 + 0); \ 819 w1 ^= 3; \ 820 if w1 != 0 goto l1_%=; \ 821 r0 = *(u64*)(r0 + 8); \ 822 l1_%=: r0 = 0; \ 823 exit; \ 824 " : 825 : __imm(bpf_map_lookup_elem), 826 __imm_addr(map_hash_8b) 827 : __clobber_all); 828 } 829 830 SEC("socket") 831 __description("bounds check for reg > 0, reg xor 3") 832 __success __failure_unpriv 833 __msg_unpriv("R0 min value is outside of the allowed memory range") 834 __retval(0) 835 __naked void reg_0_reg_xor_3(void) 836 { 837 asm volatile (" \ 838 r1 = 0; \ 839 *(u64*)(r10 - 8) = r1; \ 840 r2 = r10; \ 841 r2 += -8; \ 842 r1 = %[map_hash_8b] ll; \ 843 call %[bpf_map_lookup_elem]; \ 844 if r0 != 0 goto l0_%=; \ 845 exit; \ 846 l0_%=: r1 = *(u64*)(r0 + 0); \ 847 if r1 <= 0 goto l1_%=; \ 848 r1 ^= 3; \ 849 if r1 >= 0 goto l1_%=; \ 850 r0 = *(u64*)(r0 + 8); \ 851 l1_%=: r0 = 0; \ 852 exit; \ 853 " : 854 : __imm(bpf_map_lookup_elem), 855 __imm_addr(map_hash_8b) 856 : __clobber_all); 857 } 858 859 SEC("socket") 860 __description("bounds check for reg32 > 0, reg32 xor 3") 861 __success __failure_unpriv 862 __msg_unpriv("R0 min value is outside of the allowed memory range") 863 __retval(0) 864 __naked void reg32_0_reg32_xor_3(void) 865 { 866 asm volatile (" \ 867 r1 = 0; \ 868 *(u64*)(r10 - 8) = r1; \ 869 r2 = r10; \ 870 r2 += -8; \ 871 r1 = %[map_hash_8b] ll; \ 872 call %[bpf_map_lookup_elem]; \ 873 if r0 != 0 goto l0_%=; \ 874 exit; \ 875 l0_%=: r1 = *(u64*)(r0 + 0); \ 876 if w1 <= 0 goto l1_%=; \ 877 w1 ^= 3; \ 878 if w1 >= 0 goto l1_%=; \ 879 r0 = *(u64*)(r0 + 8); \ 880 l1_%=: r0 = 0; \ 881 exit; \ 882 " : 883 : __imm(bpf_map_lookup_elem), 884 __imm_addr(map_hash_8b) 885 : __clobber_all); 886 } 887 888 SEC("socket") 889 __description("bounds checks after 32-bit truncation. test 1") 890 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 891 __retval(0) 892 __naked void _32_bit_truncation_test_1(void) 893 { 894 asm volatile (" \ 895 r1 = 0; \ 896 *(u64*)(r10 - 8) = r1; \ 897 r2 = r10; \ 898 r2 += -8; \ 899 r1 = %[map_hash_8b] ll; \ 900 call %[bpf_map_lookup_elem]; \ 901 if r0 == 0 goto l0_%=; \ 902 r1 = *(u32*)(r0 + 0); \ 903 /* This used to reduce the max bound to 0x7fffffff */\ 904 if r1 == 0 goto l1_%=; \ 905 if r1 > 0x7fffffff goto l0_%=; \ 906 l1_%=: r0 = 0; \ 907 l0_%=: exit; \ 908 " : 909 : __imm(bpf_map_lookup_elem), 910 __imm_addr(map_hash_8b) 911 : __clobber_all); 912 } 913 914 SEC("socket") 915 __description("bounds checks after 32-bit truncation. test 2") 916 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 917 __retval(0) 918 __naked void _32_bit_truncation_test_2(void) 919 { 920 asm volatile (" \ 921 r1 = 0; \ 922 *(u64*)(r10 - 8) = r1; \ 923 r2 = r10; \ 924 r2 += -8; \ 925 r1 = %[map_hash_8b] ll; \ 926 call %[bpf_map_lookup_elem]; \ 927 if r0 == 0 goto l0_%=; \ 928 r1 = *(u32*)(r0 + 0); \ 929 if r1 s< 1 goto l1_%=; \ 930 if w1 s< 0 goto l0_%=; \ 931 l1_%=: r0 = 0; \ 932 l0_%=: exit; \ 933 " : 934 : __imm(bpf_map_lookup_elem), 935 __imm_addr(map_hash_8b) 936 : __clobber_all); 937 } 938 939 SEC("xdp") 940 __description("bound check with JMP_JLT for crossing 64-bit signed boundary") 941 __success __retval(0) 942 __naked void crossing_64_bit_signed_boundary_1(void) 943 { 944 asm volatile (" \ 945 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 946 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 947 r1 = r2; \ 948 r1 += 1; \ 949 if r1 > r3 goto l0_%=; \ 950 r1 = *(u8*)(r2 + 0); \ 951 r0 = 0x7fffffffffffff10 ll; \ 952 r1 += r0; \ 953 r0 = 0x8000000000000000 ll; \ 954 l1_%=: r0 += 1; \ 955 /* r1 unsigned range is [0x7fffffffffffff10, 0x800000000000000f] */\ 956 if r0 < r1 goto l1_%=; \ 957 l0_%=: r0 = 0; \ 958 exit; \ 959 " : 960 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 961 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 962 : __clobber_all); 963 } 964 965 SEC("xdp") 966 __description("bound check with JMP_JSLT for crossing 64-bit signed boundary") 967 __success __retval(0) 968 __naked void crossing_64_bit_signed_boundary_2(void) 969 { 970 asm volatile (" \ 971 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 972 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 973 r1 = r2; \ 974 r1 += 1; \ 975 if r1 > r3 goto l0_%=; \ 976 r1 = *(u8*)(r2 + 0); \ 977 r0 = 0x7fffffffffffff10 ll; \ 978 r1 += r0; \ 979 r2 = 0x8000000000000fff ll; \ 980 r0 = 0x8000000000000000 ll; \ 981 l1_%=: r0 += 1; \ 982 if r0 s> r2 goto l0_%=; \ 983 /* r1 signed range is [S64_MIN, S64_MAX] */ \ 984 if r0 s< r1 goto l1_%=; \ 985 r0 = 1; \ 986 exit; \ 987 l0_%=: r0 = 0; \ 988 exit; \ 989 " : 990 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 991 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 992 : __clobber_all); 993 } 994 995 SEC("xdp") 996 __description("bound check for loop upper bound greater than U32_MAX") 997 __success __retval(0) 998 __naked void bound_greater_than_u32_max(void) 999 { 1000 asm volatile (" \ 1001 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1002 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1003 r1 = r2; \ 1004 r1 += 1; \ 1005 if r1 > r3 goto l0_%=; \ 1006 r1 = *(u8*)(r2 + 0); \ 1007 r0 = 0x100000000 ll; \ 1008 r1 += r0; \ 1009 r0 = 0x100000000 ll; \ 1010 l1_%=: r0 += 1; \ 1011 if r0 < r1 goto l1_%=; \ 1012 l0_%=: r0 = 0; \ 1013 exit; \ 1014 " : 1015 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1016 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1017 : __clobber_all); 1018 } 1019 1020 SEC("xdp") 1021 __description("bound check with JMP32_JLT for crossing 32-bit signed boundary") 1022 __success __retval(0) 1023 __naked void crossing_32_bit_signed_boundary_1(void) 1024 { 1025 asm volatile (" \ 1026 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1027 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1028 r1 = r2; \ 1029 r1 += 1; \ 1030 if r1 > r3 goto l0_%=; \ 1031 r1 = *(u8*)(r2 + 0); \ 1032 w0 = 0x7fffff10; \ 1033 w1 += w0; \ 1034 w0 = 0x80000000; \ 1035 l1_%=: w0 += 1; \ 1036 /* r1 unsigned range is [0, 0x8000000f] */ \ 1037 if w0 < w1 goto l1_%=; \ 1038 l0_%=: r0 = 0; \ 1039 exit; \ 1040 " : 1041 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1042 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1043 : __clobber_all); 1044 } 1045 1046 SEC("xdp") 1047 __description("bound check with JMP32_JSLT for crossing 32-bit signed boundary") 1048 __success __retval(0) 1049 __naked void crossing_32_bit_signed_boundary_2(void) 1050 { 1051 asm volatile (" \ 1052 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1053 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1054 r1 = r2; \ 1055 r1 += 1; \ 1056 if r1 > r3 goto l0_%=; \ 1057 r1 = *(u8*)(r2 + 0); \ 1058 w0 = 0x7fffff10; \ 1059 w1 += w0; \ 1060 w2 = 0x80000fff; \ 1061 w0 = 0x80000000; \ 1062 l1_%=: w0 += 1; \ 1063 if w0 s> w2 goto l0_%=; \ 1064 /* r1 signed range is [S32_MIN, S32_MAX] */ \ 1065 if w0 s< w1 goto l1_%=; \ 1066 r0 = 1; \ 1067 exit; \ 1068 l0_%=: r0 = 0; \ 1069 exit; \ 1070 " : 1071 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1072 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1073 : __clobber_all); 1074 } 1075 1076 char _license[] SEC("license") = "GPL"; 1077