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 <../../../include/linux/filter.h> 6 #include <bpf/bpf_helpers.h> 7 #include "bpf_misc.h" 8 9 struct { 10 __uint(type, BPF_MAP_TYPE_HASH); 11 __uint(max_entries, 1); 12 __type(key, long long); 13 __type(value, long long); 14 } map_hash_8b SEC(".maps"); 15 16 SEC("socket") 17 __description("subtraction bounds (map value) variant 1") 18 __failure __msg("R0 max value is outside of the allowed memory range") 19 __failure_unpriv 20 __naked void bounds_map_value_variant_1(void) 21 { 22 asm volatile (" \ 23 r1 = 0; \ 24 *(u64*)(r10 - 8) = r1; \ 25 r2 = r10; \ 26 r2 += -8; \ 27 r1 = %[map_hash_8b] ll; \ 28 call %[bpf_map_lookup_elem]; \ 29 if r0 == 0 goto l0_%=; \ 30 r1 = *(u8*)(r0 + 0); \ 31 if r1 > 0xff goto l0_%=; \ 32 r3 = *(u8*)(r0 + 1); \ 33 if r3 > 0xff goto l0_%=; \ 34 r1 -= r3; \ 35 r1 >>= 56; \ 36 r0 += r1; \ 37 r0 = *(u8*)(r0 + 0); \ 38 exit; \ 39 l0_%=: r0 = 0; \ 40 exit; \ 41 " : 42 : __imm(bpf_map_lookup_elem), 43 __imm_addr(map_hash_8b) 44 : __clobber_all); 45 } 46 47 SEC("socket") 48 __description("subtraction bounds (map value) variant 2") 49 __failure 50 __msg("R0 min value is negative, either use unsigned index or do a if (index >=0) check.") 51 __msg_unpriv("R1 has unknown scalar with mixed signed bounds") 52 __naked void bounds_map_value_variant_2(void) 53 { 54 asm volatile (" \ 55 r1 = 0; \ 56 *(u64*)(r10 - 8) = r1; \ 57 r2 = r10; \ 58 r2 += -8; \ 59 r1 = %[map_hash_8b] ll; \ 60 call %[bpf_map_lookup_elem]; \ 61 if r0 == 0 goto l0_%=; \ 62 r1 = *(u8*)(r0 + 0); \ 63 if r1 > 0xff goto l0_%=; \ 64 r3 = *(u8*)(r0 + 1); \ 65 if r3 > 0xff goto l0_%=; \ 66 r1 -= r3; \ 67 r0 += r1; \ 68 r0 = *(u8*)(r0 + 0); \ 69 exit; \ 70 l0_%=: r0 = 0; \ 71 exit; \ 72 " : 73 : __imm(bpf_map_lookup_elem), 74 __imm_addr(map_hash_8b) 75 : __clobber_all); 76 } 77 78 SEC("socket") 79 __description("check subtraction on pointers for unpriv") 80 __success __failure_unpriv __msg_unpriv("R9 pointer -= pointer prohibited") 81 __retval(0) 82 __naked void subtraction_on_pointers_for_unpriv(void) 83 { 84 asm volatile (" \ 85 r0 = 0; \ 86 r1 = %[map_hash_8b] ll; \ 87 r2 = r10; \ 88 r2 += -8; \ 89 r6 = 9; \ 90 *(u64*)(r2 + 0) = r6; \ 91 call %[bpf_map_lookup_elem]; \ 92 r9 = r10; \ 93 r9 -= r0; \ 94 r1 = %[map_hash_8b] ll; \ 95 r2 = r10; \ 96 r2 += -8; \ 97 r6 = 0; \ 98 *(u64*)(r2 + 0) = r6; \ 99 call %[bpf_map_lookup_elem]; \ 100 if r0 != 0 goto l0_%=; \ 101 exit; \ 102 l0_%=: *(u64*)(r0 + 0) = r9; \ 103 r0 = 0; \ 104 exit; \ 105 " : 106 : __imm(bpf_map_lookup_elem), 107 __imm_addr(map_hash_8b) 108 : __clobber_all); 109 } 110 111 SEC("socket") 112 __description("bounds check based on zero-extended MOV") 113 __success __success_unpriv __retval(0) 114 __naked void based_on_zero_extended_mov(void) 115 { 116 asm volatile (" \ 117 r1 = 0; \ 118 *(u64*)(r10 - 8) = r1; \ 119 r2 = r10; \ 120 r2 += -8; \ 121 r1 = %[map_hash_8b] ll; \ 122 call %[bpf_map_lookup_elem]; \ 123 if r0 == 0 goto l0_%=; \ 124 /* r2 = 0x0000'0000'ffff'ffff */ \ 125 w2 = 0xffffffff; \ 126 /* r2 = 0 */ \ 127 r2 >>= 32; \ 128 /* no-op */ \ 129 r0 += r2; \ 130 /* access at offset 0 */ \ 131 r0 = *(u8*)(r0 + 0); \ 132 l0_%=: /* exit */ \ 133 r0 = 0; \ 134 exit; \ 135 " : 136 : __imm(bpf_map_lookup_elem), 137 __imm_addr(map_hash_8b) 138 : __clobber_all); 139 } 140 141 SEC("socket") 142 __description("bounds check based on sign-extended MOV. test1") 143 __failure __msg("map_value pointer and 4294967295") 144 __failure_unpriv 145 __naked void on_sign_extended_mov_test1(void) 146 { 147 asm volatile (" \ 148 r1 = 0; \ 149 *(u64*)(r10 - 8) = r1; \ 150 r2 = r10; \ 151 r2 += -8; \ 152 r1 = %[map_hash_8b] ll; \ 153 call %[bpf_map_lookup_elem]; \ 154 if r0 == 0 goto l0_%=; \ 155 /* r2 = 0xffff'ffff'ffff'ffff */ \ 156 r2 = 0xffffffff; \ 157 /* r2 = 0xffff'ffff */ \ 158 r2 >>= 32; \ 159 /* r0 = <oob pointer> */ \ 160 r0 += r2; \ 161 /* access to OOB pointer */ \ 162 r0 = *(u8*)(r0 + 0); \ 163 l0_%=: /* exit */ \ 164 r0 = 0; \ 165 exit; \ 166 " : 167 : __imm(bpf_map_lookup_elem), 168 __imm_addr(map_hash_8b) 169 : __clobber_all); 170 } 171 172 SEC("socket") 173 __description("bounds check based on sign-extended MOV. test2") 174 __failure __msg("R0 min value is outside of the allowed memory range") 175 __failure_unpriv 176 __naked void on_sign_extended_mov_test2(void) 177 { 178 asm volatile (" \ 179 r1 = 0; \ 180 *(u64*)(r10 - 8) = r1; \ 181 r2 = r10; \ 182 r2 += -8; \ 183 r1 = %[map_hash_8b] ll; \ 184 call %[bpf_map_lookup_elem]; \ 185 if r0 == 0 goto l0_%=; \ 186 /* r2 = 0xffff'ffff'ffff'ffff */ \ 187 r2 = 0xffffffff; \ 188 /* r2 = 0xfff'ffff */ \ 189 r2 >>= 36; \ 190 /* r0 = <oob pointer> */ \ 191 r0 += r2; \ 192 /* access to OOB pointer */ \ 193 r0 = *(u8*)(r0 + 0); \ 194 l0_%=: /* exit */ \ 195 r0 = 0; \ 196 exit; \ 197 " : 198 : __imm(bpf_map_lookup_elem), 199 __imm_addr(map_hash_8b) 200 : __clobber_all); 201 } 202 203 SEC("tc") 204 __description("bounds check based on reg_off + var_off + insn_off. test1") 205 __failure __msg("map_value pointer offset 1073741822 is not allowed") 206 __naked void var_off_insn_off_test1(void) 207 { 208 asm volatile (" \ 209 r6 = *(u32*)(r1 + %[__sk_buff_mark]); \ 210 r1 = 0; \ 211 *(u64*)(r10 - 8) = r1; \ 212 r2 = r10; \ 213 r2 += -8; \ 214 r1 = %[map_hash_8b] ll; \ 215 call %[bpf_map_lookup_elem]; \ 216 if r0 == 0 goto l0_%=; \ 217 r6 &= 1; \ 218 r6 += %[__imm_0]; \ 219 r0 += r6; \ 220 r0 += %[__imm_0]; \ 221 l0_%=: r0 = *(u8*)(r0 + 3); \ 222 r0 = 0; \ 223 exit; \ 224 " : 225 : __imm(bpf_map_lookup_elem), 226 __imm_addr(map_hash_8b), 227 __imm_const(__imm_0, (1 << 29) - 1), 228 __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)) 229 : __clobber_all); 230 } 231 232 SEC("tc") 233 __description("bounds check based on reg_off + var_off + insn_off. test2") 234 __failure __msg("value 1073741823") 235 __naked void var_off_insn_off_test2(void) 236 { 237 asm volatile (" \ 238 r6 = *(u32*)(r1 + %[__sk_buff_mark]); \ 239 r1 = 0; \ 240 *(u64*)(r10 - 8) = r1; \ 241 r2 = r10; \ 242 r2 += -8; \ 243 r1 = %[map_hash_8b] ll; \ 244 call %[bpf_map_lookup_elem]; \ 245 if r0 == 0 goto l0_%=; \ 246 r6 &= 1; \ 247 r6 += %[__imm_0]; \ 248 r0 += r6; \ 249 r0 += %[__imm_1]; \ 250 l0_%=: r0 = *(u8*)(r0 + 3); \ 251 r0 = 0; \ 252 exit; \ 253 " : 254 : __imm(bpf_map_lookup_elem), 255 __imm_addr(map_hash_8b), 256 __imm_const(__imm_0, (1 << 30) - 1), 257 __imm_const(__imm_1, (1 << 29) - 1), 258 __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)) 259 : __clobber_all); 260 } 261 262 SEC("socket") 263 __description("bounds check after truncation of non-boundary-crossing range") 264 __success __success_unpriv __retval(0) 265 __naked void of_non_boundary_crossing_range(void) 266 { 267 asm volatile (" \ 268 r1 = 0; \ 269 *(u64*)(r10 - 8) = r1; \ 270 r2 = r10; \ 271 r2 += -8; \ 272 r1 = %[map_hash_8b] ll; \ 273 call %[bpf_map_lookup_elem]; \ 274 if r0 == 0 goto l0_%=; \ 275 /* r1 = [0x00, 0xff] */ \ 276 r1 = *(u8*)(r0 + 0); \ 277 r2 = 1; \ 278 /* r2 = 0x10'0000'0000 */ \ 279 r2 <<= 36; \ 280 /* r1 = [0x10'0000'0000, 0x10'0000'00ff] */ \ 281 r1 += r2; \ 282 /* r1 = [0x10'7fff'ffff, 0x10'8000'00fe] */ \ 283 r1 += 0x7fffffff; \ 284 /* r1 = [0x00, 0xff] */ \ 285 w1 -= 0x7fffffff; \ 286 /* r1 = 0 */ \ 287 r1 >>= 8; \ 288 /* no-op */ \ 289 r0 += r1; \ 290 /* access at offset 0 */ \ 291 r0 = *(u8*)(r0 + 0); \ 292 l0_%=: /* exit */ \ 293 r0 = 0; \ 294 exit; \ 295 " : 296 : __imm(bpf_map_lookup_elem), 297 __imm_addr(map_hash_8b) 298 : __clobber_all); 299 } 300 301 SEC("socket") 302 __description("bounds check after truncation of boundary-crossing range (1)") 303 __failure 304 /* not actually fully unbounded, but the bound is very high */ 305 __msg("value -4294967168 makes map_value pointer be out of bounds") 306 __failure_unpriv 307 __naked void of_boundary_crossing_range_1(void) 308 { 309 asm volatile (" \ 310 r1 = 0; \ 311 *(u64*)(r10 - 8) = r1; \ 312 r2 = r10; \ 313 r2 += -8; \ 314 r1 = %[map_hash_8b] ll; \ 315 call %[bpf_map_lookup_elem]; \ 316 if r0 == 0 goto l0_%=; \ 317 /* r1 = [0x00, 0xff] */ \ 318 r1 = *(u8*)(r0 + 0); \ 319 r1 += %[__imm_0]; \ 320 /* r1 = [0xffff'ff80, 0x1'0000'007f] */ \ 321 r1 += %[__imm_0]; \ 322 /* r1 = [0xffff'ff80, 0xffff'ffff] or \ 323 * [0x0000'0000, 0x0000'007f] \ 324 */ \ 325 w1 += 0; \ 326 r1 -= %[__imm_0]; \ 327 /* r1 = [0x00, 0xff] or \ 328 * [0xffff'ffff'0000'0080, 0xffff'ffff'ffff'ffff]\ 329 */ \ 330 r1 -= %[__imm_0]; \ 331 /* error on OOB pointer computation */ \ 332 r0 += r1; \ 333 /* exit */ \ 334 r0 = 0; \ 335 l0_%=: exit; \ 336 " : 337 : __imm(bpf_map_lookup_elem), 338 __imm_addr(map_hash_8b), 339 __imm_const(__imm_0, 0xffffff80 >> 1) 340 : __clobber_all); 341 } 342 343 SEC("socket") 344 __description("bounds check after truncation of boundary-crossing range (2)") 345 __failure __msg("value -4294967168 makes map_value pointer be out of bounds") 346 __failure_unpriv 347 __naked void of_boundary_crossing_range_2(void) 348 { 349 asm volatile (" \ 350 r1 = 0; \ 351 *(u64*)(r10 - 8) = r1; \ 352 r2 = r10; \ 353 r2 += -8; \ 354 r1 = %[map_hash_8b] ll; \ 355 call %[bpf_map_lookup_elem]; \ 356 if r0 == 0 goto l0_%=; \ 357 /* r1 = [0x00, 0xff] */ \ 358 r1 = *(u8*)(r0 + 0); \ 359 r1 += %[__imm_0]; \ 360 /* r1 = [0xffff'ff80, 0x1'0000'007f] */ \ 361 r1 += %[__imm_0]; \ 362 /* r1 = [0xffff'ff80, 0xffff'ffff] or \ 363 * [0x0000'0000, 0x0000'007f] \ 364 * difference to previous test: truncation via MOV32\ 365 * instead of ALU32. \ 366 */ \ 367 w1 = w1; \ 368 r1 -= %[__imm_0]; \ 369 /* r1 = [0x00, 0xff] or \ 370 * [0xffff'ffff'0000'0080, 0xffff'ffff'ffff'ffff]\ 371 */ \ 372 r1 -= %[__imm_0]; \ 373 /* error on OOB pointer computation */ \ 374 r0 += r1; \ 375 /* exit */ \ 376 r0 = 0; \ 377 l0_%=: exit; \ 378 " : 379 : __imm(bpf_map_lookup_elem), 380 __imm_addr(map_hash_8b), 381 __imm_const(__imm_0, 0xffffff80 >> 1) 382 : __clobber_all); 383 } 384 385 SEC("socket") 386 __description("bounds check after wrapping 32-bit addition") 387 __success __success_unpriv __retval(0) 388 __naked void after_wrapping_32_bit_addition(void) 389 { 390 asm volatile (" \ 391 r1 = 0; \ 392 *(u64*)(r10 - 8) = r1; \ 393 r2 = r10; \ 394 r2 += -8; \ 395 r1 = %[map_hash_8b] ll; \ 396 call %[bpf_map_lookup_elem]; \ 397 if r0 == 0 goto l0_%=; \ 398 /* r1 = 0x7fff'ffff */ \ 399 r1 = 0x7fffffff; \ 400 /* r1 = 0xffff'fffe */ \ 401 r1 += 0x7fffffff; \ 402 /* r1 = 0 */ \ 403 w1 += 2; \ 404 /* no-op */ \ 405 r0 += r1; \ 406 /* access at offset 0 */ \ 407 r0 = *(u8*)(r0 + 0); \ 408 l0_%=: /* exit */ \ 409 r0 = 0; \ 410 exit; \ 411 " : 412 : __imm(bpf_map_lookup_elem), 413 __imm_addr(map_hash_8b) 414 : __clobber_all); 415 } 416 417 SEC("socket") 418 __description("bounds check after shift with oversized count operand") 419 __failure __msg("R0 max value is outside of the allowed memory range") 420 __failure_unpriv 421 __naked void shift_with_oversized_count_operand(void) 422 { 423 asm volatile (" \ 424 r1 = 0; \ 425 *(u64*)(r10 - 8) = r1; \ 426 r2 = r10; \ 427 r2 += -8; \ 428 r1 = %[map_hash_8b] ll; \ 429 call %[bpf_map_lookup_elem]; \ 430 if r0 == 0 goto l0_%=; \ 431 r2 = 32; \ 432 r1 = 1; \ 433 /* r1 = (u32)1 << (u32)32 = ? */ \ 434 w1 <<= w2; \ 435 /* r1 = [0x0000, 0xffff] */ \ 436 r1 &= 0xffff; \ 437 /* computes unknown pointer, potentially OOB */ \ 438 r0 += r1; \ 439 /* potentially OOB access */ \ 440 r0 = *(u8*)(r0 + 0); \ 441 l0_%=: /* exit */ \ 442 r0 = 0; \ 443 exit; \ 444 " : 445 : __imm(bpf_map_lookup_elem), 446 __imm_addr(map_hash_8b) 447 : __clobber_all); 448 } 449 450 SEC("socket") 451 __description("bounds check after right shift of maybe-negative number") 452 __failure __msg("R0 unbounded memory access") 453 __failure_unpriv 454 __naked void shift_of_maybe_negative_number(void) 455 { 456 asm volatile (" \ 457 r1 = 0; \ 458 *(u64*)(r10 - 8) = r1; \ 459 r2 = r10; \ 460 r2 += -8; \ 461 r1 = %[map_hash_8b] ll; \ 462 call %[bpf_map_lookup_elem]; \ 463 if r0 == 0 goto l0_%=; \ 464 /* r1 = [0x00, 0xff] */ \ 465 r1 = *(u8*)(r0 + 0); \ 466 /* r1 = [-0x01, 0xfe] */ \ 467 r1 -= 1; \ 468 /* r1 = 0 or 0xff'ffff'ffff'ffff */ \ 469 r1 >>= 8; \ 470 /* r1 = 0 or 0xffff'ffff'ffff */ \ 471 r1 >>= 8; \ 472 /* computes unknown pointer, potentially OOB */ \ 473 r0 += r1; \ 474 /* potentially OOB access */ \ 475 r0 = *(u8*)(r0 + 0); \ 476 l0_%=: /* exit */ \ 477 r0 = 0; \ 478 exit; \ 479 " : 480 : __imm(bpf_map_lookup_elem), 481 __imm_addr(map_hash_8b) 482 : __clobber_all); 483 } 484 485 SEC("socket") 486 __description("bounds check after 32-bit right shift with 64-bit input") 487 __failure __msg("math between map_value pointer and 4294967294 is not allowed") 488 __failure_unpriv 489 __naked void shift_with_64_bit_input(void) 490 { 491 asm volatile (" \ 492 r1 = 0; \ 493 *(u64*)(r10 - 8) = r1; \ 494 r2 = r10; \ 495 r2 += -8; \ 496 r1 = %[map_hash_8b] ll; \ 497 call %[bpf_map_lookup_elem]; \ 498 if r0 == 0 goto l0_%=; \ 499 r1 = 2; \ 500 /* r1 = 1<<32 */ \ 501 r1 <<= 31; \ 502 /* r1 = 0 (NOT 2!) */ \ 503 w1 >>= 31; \ 504 /* r1 = 0xffff'fffe (NOT 0!) */ \ 505 w1 -= 2; \ 506 /* error on computing OOB pointer */ \ 507 r0 += r1; \ 508 /* exit */ \ 509 r0 = 0; \ 510 l0_%=: exit; \ 511 " : 512 : __imm(bpf_map_lookup_elem), 513 __imm_addr(map_hash_8b) 514 : __clobber_all); 515 } 516 517 SEC("socket") 518 __description("bounds check map access with off+size signed 32bit overflow. test1") 519 __failure __msg("map_value pointer and 2147483646") 520 __failure_unpriv 521 __naked void size_signed_32bit_overflow_test1(void) 522 { 523 asm volatile (" \ 524 r1 = 0; \ 525 *(u64*)(r10 - 8) = r1; \ 526 r2 = r10; \ 527 r2 += -8; \ 528 r1 = %[map_hash_8b] ll; \ 529 call %[bpf_map_lookup_elem]; \ 530 if r0 != 0 goto l0_%=; \ 531 exit; \ 532 l0_%=: r0 += 0x7ffffffe; \ 533 r0 = *(u64*)(r0 + 0); \ 534 goto l1_%=; \ 535 l1_%=: exit; \ 536 " : 537 : __imm(bpf_map_lookup_elem), 538 __imm_addr(map_hash_8b) 539 : __clobber_all); 540 } 541 542 SEC("socket") 543 __description("bounds check map access with off+size signed 32bit overflow. test2") 544 __failure __msg("pointer offset 1073741822") 545 __msg_unpriv("R0 pointer arithmetic of map value goes out of range") 546 __naked void size_signed_32bit_overflow_test2(void) 547 { 548 asm volatile (" \ 549 r1 = 0; \ 550 *(u64*)(r10 - 8) = r1; \ 551 r2 = r10; \ 552 r2 += -8; \ 553 r1 = %[map_hash_8b] ll; \ 554 call %[bpf_map_lookup_elem]; \ 555 if r0 != 0 goto l0_%=; \ 556 exit; \ 557 l0_%=: r0 += 0x1fffffff; \ 558 r0 += 0x1fffffff; \ 559 r0 += 0x1fffffff; \ 560 r0 = *(u64*)(r0 + 0); \ 561 goto l1_%=; \ 562 l1_%=: exit; \ 563 " : 564 : __imm(bpf_map_lookup_elem), 565 __imm_addr(map_hash_8b) 566 : __clobber_all); 567 } 568 569 SEC("socket") 570 __description("bounds check map access with off+size signed 32bit overflow. test3") 571 __failure __msg("pointer offset -1073741822") 572 __msg_unpriv("R0 pointer arithmetic of map value goes out of range") 573 __naked void size_signed_32bit_overflow_test3(void) 574 { 575 asm volatile (" \ 576 r1 = 0; \ 577 *(u64*)(r10 - 8) = r1; \ 578 r2 = r10; \ 579 r2 += -8; \ 580 r1 = %[map_hash_8b] ll; \ 581 call %[bpf_map_lookup_elem]; \ 582 if r0 != 0 goto l0_%=; \ 583 exit; \ 584 l0_%=: r0 -= 0x1fffffff; \ 585 r0 -= 0x1fffffff; \ 586 r0 = *(u64*)(r0 + 2); \ 587 goto l1_%=; \ 588 l1_%=: exit; \ 589 " : 590 : __imm(bpf_map_lookup_elem), 591 __imm_addr(map_hash_8b) 592 : __clobber_all); 593 } 594 595 SEC("socket") 596 __description("bounds check map access with off+size signed 32bit overflow. test4") 597 __failure __msg("map_value pointer and 1000000000000") 598 __failure_unpriv 599 __naked void size_signed_32bit_overflow_test4(void) 600 { 601 asm volatile (" \ 602 r1 = 0; \ 603 *(u64*)(r10 - 8) = r1; \ 604 r2 = r10; \ 605 r2 += -8; \ 606 r1 = %[map_hash_8b] ll; \ 607 call %[bpf_map_lookup_elem]; \ 608 if r0 != 0 goto l0_%=; \ 609 exit; \ 610 l0_%=: r1 = 1000000; \ 611 r1 *= 1000000; \ 612 r0 += r1; \ 613 r0 = *(u64*)(r0 + 2); \ 614 goto l1_%=; \ 615 l1_%=: exit; \ 616 " : 617 : __imm(bpf_map_lookup_elem), 618 __imm_addr(map_hash_8b) 619 : __clobber_all); 620 } 621 622 SEC("socket") 623 __description("bounds check mixed 32bit and 64bit arithmetic. test1") 624 __success __success_unpriv 625 __retval(0) 626 #ifdef SPEC_V1 627 __xlated_unpriv("goto pc+2") 628 __xlated_unpriv("nospec") /* inserted to prevent `R0 invalid mem access 'scalar'` */ 629 __xlated_unpriv("goto pc-1") /* sanitized dead code */ 630 __xlated_unpriv("exit") 631 #endif 632 __naked void _32bit_and_64bit_arithmetic_test1(void) 633 { 634 asm volatile (" \ 635 r0 = 0; \ 636 r1 = -1; \ 637 r1 <<= 32; \ 638 r1 += 1; \ 639 /* r1 = 0xffffFFFF00000001 */ \ 640 if w1 > 1 goto l0_%=; \ 641 /* check ALU64 op keeps 32bit bounds */ \ 642 r1 += 1; \ 643 if w1 > 2 goto l0_%=; \ 644 goto l1_%=; \ 645 l0_%=: /* invalid ldx if bounds are lost above */ \ 646 r0 = *(u64*)(r0 - 1); \ 647 l1_%=: exit; \ 648 " ::: __clobber_all); 649 } 650 651 SEC("socket") 652 __description("bounds check mixed 32bit and 64bit arithmetic. test2") 653 __success __success_unpriv 654 __retval(0) 655 #ifdef SPEC_V1 656 __xlated_unpriv("goto pc+2") 657 __xlated_unpriv("nospec") /* inserted to prevent `R0 invalid mem access 'scalar'` */ 658 __xlated_unpriv("goto pc-1") /* sanitized dead code */ 659 __xlated_unpriv("exit") 660 #endif 661 __naked void _32bit_and_64bit_arithmetic_test2(void) 662 { 663 asm volatile (" \ 664 r0 = 0; \ 665 r1 = -1; \ 666 r1 <<= 32; \ 667 r1 += 1; \ 668 /* r1 = 0xffffFFFF00000001 */ \ 669 r2 = 3; \ 670 /* r1 = 0x2 */ \ 671 w1 += 1; \ 672 /* check ALU32 op zero extends 64bit bounds */ \ 673 if r1 > r2 goto l0_%=; \ 674 goto l1_%=; \ 675 l0_%=: /* invalid ldx if bounds are lost above */ \ 676 r0 = *(u64*)(r0 - 1); \ 677 l1_%=: exit; \ 678 " ::: __clobber_all); 679 } 680 681 SEC("tc") 682 __description("assigning 32bit bounds to 64bit for wA = 0, wB = wA") 683 __success __retval(0) __flag(BPF_F_ANY_ALIGNMENT) 684 __naked void for_wa_0_wb_wa(void) 685 { 686 asm volatile (" \ 687 r8 = *(u32*)(r1 + %[__sk_buff_data_end]); \ 688 r7 = *(u32*)(r1 + %[__sk_buff_data]); \ 689 w9 = 0; \ 690 w2 = w9; \ 691 r6 = r7; \ 692 r6 += r2; \ 693 r3 = r6; \ 694 r3 += 8; \ 695 if r3 > r8 goto l0_%=; \ 696 r5 = *(u32*)(r6 + 0); \ 697 l0_%=: r0 = 0; \ 698 exit; \ 699 " : 700 : __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)), 701 __imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)) 702 : __clobber_all); 703 } 704 705 SEC("socket") 706 __description("bounds check for reg = 0, reg xor 1") 707 __success __success_unpriv 708 __retval(0) 709 #ifdef SPEC_V1 710 __xlated_unpriv("if r1 != 0x0 goto pc+2") 711 __xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ 712 __xlated_unpriv("goto pc-1") /* sanitized dead code */ 713 __xlated_unpriv("r0 = 0") 714 #endif 715 __naked void reg_0_reg_xor_1(void) 716 { 717 asm volatile (" \ 718 r1 = 0; \ 719 *(u64*)(r10 - 8) = r1; \ 720 r2 = r10; \ 721 r2 += -8; \ 722 r1 = %[map_hash_8b] ll; \ 723 call %[bpf_map_lookup_elem]; \ 724 if r0 != 0 goto l0_%=; \ 725 exit; \ 726 l0_%=: r1 = 0; \ 727 r1 ^= 1; \ 728 if r1 != 0 goto l1_%=; \ 729 r0 = *(u64*)(r0 + 8); \ 730 l1_%=: r0 = 0; \ 731 exit; \ 732 " : 733 : __imm(bpf_map_lookup_elem), 734 __imm_addr(map_hash_8b) 735 : __clobber_all); 736 } 737 738 SEC("socket") 739 __description("bounds check for reg32 = 0, reg32 xor 1") 740 __success __success_unpriv 741 __retval(0) 742 #ifdef SPEC_V1 743 __xlated_unpriv("if w1 != 0x0 goto pc+2") 744 __xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ 745 __xlated_unpriv("goto pc-1") /* sanitized dead code */ 746 __xlated_unpriv("r0 = 0") 747 #endif 748 __naked void reg32_0_reg32_xor_1(void) 749 { 750 asm volatile (" \ 751 r1 = 0; \ 752 *(u64*)(r10 - 8) = r1; \ 753 r2 = r10; \ 754 r2 += -8; \ 755 r1 = %[map_hash_8b] ll; \ 756 call %[bpf_map_lookup_elem]; \ 757 if r0 != 0 goto l0_%=; \ 758 exit; \ 759 l0_%=: w1 = 0; \ 760 w1 ^= 1; \ 761 if w1 != 0 goto l1_%=; \ 762 r0 = *(u64*)(r0 + 8); \ 763 l1_%=: r0 = 0; \ 764 exit; \ 765 " : 766 : __imm(bpf_map_lookup_elem), 767 __imm_addr(map_hash_8b) 768 : __clobber_all); 769 } 770 771 SEC("socket") 772 __description("bounds check for reg = 2, reg xor 3") 773 __success __success_unpriv 774 __retval(0) 775 #ifdef SPEC_V1 776 __xlated_unpriv("if r1 > 0x0 goto pc+2") 777 __xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ 778 __xlated_unpriv("goto pc-1") /* sanitized dead code */ 779 __xlated_unpriv("r0 = 0") 780 #endif 781 __naked void reg_2_reg_xor_3(void) 782 { 783 asm volatile (" \ 784 r1 = 0; \ 785 *(u64*)(r10 - 8) = r1; \ 786 r2 = r10; \ 787 r2 += -8; \ 788 r1 = %[map_hash_8b] ll; \ 789 call %[bpf_map_lookup_elem]; \ 790 if r0 != 0 goto l0_%=; \ 791 exit; \ 792 l0_%=: r1 = 2; \ 793 r1 ^= 3; \ 794 if r1 > 0 goto l1_%=; \ 795 r0 = *(u64*)(r0 + 8); \ 796 l1_%=: r0 = 0; \ 797 exit; \ 798 " : 799 : __imm(bpf_map_lookup_elem), 800 __imm_addr(map_hash_8b) 801 : __clobber_all); 802 } 803 804 SEC("socket") 805 __description("bounds check for reg = any, reg xor 3") 806 __failure __msg("invalid access to map value") 807 __msg_unpriv("invalid access to map value") 808 __naked void reg_any_reg_xor_3(void) 809 { 810 asm volatile (" \ 811 r1 = 0; \ 812 *(u64*)(r10 - 8) = r1; \ 813 r2 = r10; \ 814 r2 += -8; \ 815 r1 = %[map_hash_8b] ll; \ 816 call %[bpf_map_lookup_elem]; \ 817 if r0 != 0 goto l0_%=; \ 818 exit; \ 819 l0_%=: r1 = *(u64*)(r0 + 0); \ 820 r1 ^= 3; \ 821 if r1 != 0 goto l1_%=; \ 822 r0 = *(u64*)(r0 + 8); \ 823 l1_%=: r0 = 0; \ 824 exit; \ 825 " : 826 : __imm(bpf_map_lookup_elem), 827 __imm_addr(map_hash_8b) 828 : __clobber_all); 829 } 830 831 SEC("socket") 832 __description("bounds check for reg32 = any, reg32 xor 3") 833 __failure __msg("invalid access to map value") 834 __msg_unpriv("invalid access to map value") 835 __naked void reg32_any_reg32_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 w1 ^= 3; \ 848 if w1 != 0 goto l1_%=; \ 849 r0 = *(u64*)(r0 + 8); \ 850 l1_%=: r0 = 0; \ 851 exit; \ 852 " : 853 : __imm(bpf_map_lookup_elem), 854 __imm_addr(map_hash_8b) 855 : __clobber_all); 856 } 857 858 SEC("socket") 859 __description("bounds check for reg > 0, reg xor 3") 860 __success __success_unpriv 861 __retval(0) 862 #ifdef SPEC_V1 863 __xlated_unpriv("if r1 >= 0x0 goto pc+2") 864 __xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ 865 __xlated_unpriv("goto pc-1") /* sanitized dead code */ 866 __xlated_unpriv("r0 = 0") 867 #endif 868 __naked void reg_0_reg_xor_3(void) 869 { 870 asm volatile (" \ 871 r1 = 0; \ 872 *(u64*)(r10 - 8) = r1; \ 873 r2 = r10; \ 874 r2 += -8; \ 875 r1 = %[map_hash_8b] ll; \ 876 call %[bpf_map_lookup_elem]; \ 877 if r0 != 0 goto l0_%=; \ 878 exit; \ 879 l0_%=: r1 = *(u64*)(r0 + 0); \ 880 if r1 <= 0 goto l1_%=; \ 881 r1 ^= 3; \ 882 if r1 >= 0 goto l1_%=; \ 883 r0 = *(u64*)(r0 + 8); \ 884 l1_%=: r0 = 0; \ 885 exit; \ 886 " : 887 : __imm(bpf_map_lookup_elem), 888 __imm_addr(map_hash_8b) 889 : __clobber_all); 890 } 891 892 SEC("socket") 893 __description("bounds check for reg32 > 0, reg32 xor 3") 894 __success __success_unpriv 895 __retval(0) 896 #ifdef SPEC_V1 897 __xlated_unpriv("if w1 >= 0x0 goto pc+2") 898 __xlated_unpriv("nospec") /* inserted to prevent `R0 min value is outside of the allowed memory range` */ 899 __xlated_unpriv("goto pc-1") /* sanitized dead code */ 900 __xlated_unpriv("r0 = 0") 901 #endif 902 __naked void reg32_0_reg32_xor_3(void) 903 { 904 asm volatile (" \ 905 r1 = 0; \ 906 *(u64*)(r10 - 8) = r1; \ 907 r2 = r10; \ 908 r2 += -8; \ 909 r1 = %[map_hash_8b] ll; \ 910 call %[bpf_map_lookup_elem]; \ 911 if r0 != 0 goto l0_%=; \ 912 exit; \ 913 l0_%=: r1 = *(u64*)(r0 + 0); \ 914 if w1 <= 0 goto l1_%=; \ 915 w1 ^= 3; \ 916 if w1 >= 0 goto l1_%=; \ 917 r0 = *(u64*)(r0 + 8); \ 918 l1_%=: r0 = 0; \ 919 exit; \ 920 " : 921 : __imm(bpf_map_lookup_elem), 922 __imm_addr(map_hash_8b) 923 : __clobber_all); 924 } 925 926 SEC("socket") 927 __description("bounds check for non const xor src dst") 928 __success __log_level(2) 929 __msg("5: (af) r0 ^= r6 ; R0=scalar(smin=smin32=0,smax=umax=smax32=umax32=431,var_off=(0x0; 0x1af))") 930 __naked void non_const_xor_src_dst(void) 931 { 932 asm volatile (" \ 933 call %[bpf_get_prandom_u32]; \ 934 r6 = r0; \ 935 call %[bpf_get_prandom_u32]; \ 936 r6 &= 0xaf; \ 937 r0 &= 0x1a0; \ 938 r0 ^= r6; \ 939 exit; \ 940 " : 941 : __imm(bpf_map_lookup_elem), 942 __imm_addr(map_hash_8b), 943 __imm(bpf_get_prandom_u32) 944 : __clobber_all); 945 } 946 947 SEC("socket") 948 __description("bounds check for non const or src dst") 949 __success __log_level(2) 950 __msg("5: (4f) r0 |= r6 ; R0=scalar(smin=smin32=0,smax=umax=smax32=umax32=431,var_off=(0x0; 0x1af))") 951 __naked void non_const_or_src_dst(void) 952 { 953 asm volatile (" \ 954 call %[bpf_get_prandom_u32]; \ 955 r6 = r0; \ 956 call %[bpf_get_prandom_u32]; \ 957 r6 &= 0xaf; \ 958 r0 &= 0x1a0; \ 959 r0 |= r6; \ 960 exit; \ 961 " : 962 : __imm(bpf_map_lookup_elem), 963 __imm_addr(map_hash_8b), 964 __imm(bpf_get_prandom_u32) 965 : __clobber_all); 966 } 967 968 SEC("socket") 969 __description("bounds check for non const mul regs") 970 __success __log_level(2) 971 __msg("5: (2f) r0 *= r6 ; R0=scalar(smin=smin32=0,smax=umax=smax32=umax32=3825,var_off=(0x0; 0xfff))") 972 __naked void non_const_mul_regs(void) 973 { 974 asm volatile (" \ 975 call %[bpf_get_prandom_u32]; \ 976 r6 = r0; \ 977 call %[bpf_get_prandom_u32]; \ 978 r6 &= 0xff; \ 979 r0 &= 0x0f; \ 980 r0 *= r6; \ 981 exit; \ 982 " : 983 : __imm(bpf_map_lookup_elem), 984 __imm_addr(map_hash_8b), 985 __imm(bpf_get_prandom_u32) 986 : __clobber_all); 987 } 988 989 SEC("socket") 990 __description("bounds checks after 32-bit truncation. test 1") 991 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 992 __retval(0) 993 __naked void _32_bit_truncation_test_1(void) 994 { 995 asm volatile (" \ 996 r1 = 0; \ 997 *(u64*)(r10 - 8) = r1; \ 998 r2 = r10; \ 999 r2 += -8; \ 1000 r1 = %[map_hash_8b] ll; \ 1001 call %[bpf_map_lookup_elem]; \ 1002 if r0 == 0 goto l0_%=; \ 1003 r1 = *(u32*)(r0 + 0); \ 1004 /* This used to reduce the max bound to 0x7fffffff */\ 1005 if r1 == 0 goto l1_%=; \ 1006 if r1 > 0x7fffffff goto l0_%=; \ 1007 l1_%=: r0 = 0; \ 1008 l0_%=: exit; \ 1009 " : 1010 : __imm(bpf_map_lookup_elem), 1011 __imm_addr(map_hash_8b) 1012 : __clobber_all); 1013 } 1014 1015 SEC("socket") 1016 __description("bounds checks after 32-bit truncation. test 2") 1017 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 1018 __retval(0) 1019 __naked void _32_bit_truncation_test_2(void) 1020 { 1021 asm volatile (" \ 1022 r1 = 0; \ 1023 *(u64*)(r10 - 8) = r1; \ 1024 r2 = r10; \ 1025 r2 += -8; \ 1026 r1 = %[map_hash_8b] ll; \ 1027 call %[bpf_map_lookup_elem]; \ 1028 if r0 == 0 goto l0_%=; \ 1029 r1 = *(u32*)(r0 + 0); \ 1030 if r1 s< 1 goto l1_%=; \ 1031 if w1 s< 0 goto l0_%=; \ 1032 l1_%=: r0 = 0; \ 1033 l0_%=: exit; \ 1034 " : 1035 : __imm(bpf_map_lookup_elem), 1036 __imm_addr(map_hash_8b) 1037 : __clobber_all); 1038 } 1039 1040 SEC("xdp") 1041 __description("bound check with JMP_JLT for crossing 64-bit signed boundary") 1042 __success __retval(0) 1043 __naked void crossing_64_bit_signed_boundary_1(void) 1044 { 1045 asm volatile (" \ 1046 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1047 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1048 r1 = r2; \ 1049 r1 += 1; \ 1050 if r1 > r3 goto l0_%=; \ 1051 r1 = *(u8*)(r2 + 0); \ 1052 r0 = 0x7fffffffffffff10 ll; \ 1053 r1 += r0; \ 1054 r0 = 0x8000000000000000 ll; \ 1055 l1_%=: r0 += 1; \ 1056 /* r1 unsigned range is [0x7fffffffffffff10, 0x800000000000000f] */\ 1057 if r0 < r1 goto l1_%=; \ 1058 l0_%=: r0 = 0; \ 1059 exit; \ 1060 " : 1061 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1062 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1063 : __clobber_all); 1064 } 1065 1066 SEC("xdp") 1067 __description("bound check with JMP_JSLT for crossing 64-bit signed boundary") 1068 __success __retval(0) 1069 __naked void crossing_64_bit_signed_boundary_2(void) 1070 { 1071 asm volatile (" \ 1072 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1073 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1074 r1 = r2; \ 1075 r1 += 1; \ 1076 if r1 > r3 goto l0_%=; \ 1077 r1 = *(u8*)(r2 + 0); \ 1078 r0 = 0x7fffffffffffff10 ll; \ 1079 r1 += r0; \ 1080 r2 = 0x8000000000000fff ll; \ 1081 r0 = 0x8000000000000000 ll; \ 1082 l1_%=: r0 += 1; \ 1083 if r0 s> r2 goto l0_%=; \ 1084 /* r1 signed range is [S64_MIN, S64_MAX] */ \ 1085 if r0 s< r1 goto l1_%=; \ 1086 r0 = 1; \ 1087 exit; \ 1088 l0_%=: r0 = 0; \ 1089 exit; \ 1090 " : 1091 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1092 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1093 : __clobber_all); 1094 } 1095 1096 SEC("xdp") 1097 __description("bound check for loop upper bound greater than U32_MAX") 1098 __success __retval(0) 1099 __naked void bound_greater_than_u32_max(void) 1100 { 1101 asm volatile (" \ 1102 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1103 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1104 r1 = r2; \ 1105 r1 += 1; \ 1106 if r1 > r3 goto l0_%=; \ 1107 r1 = *(u8*)(r2 + 0); \ 1108 r0 = 0x100000000 ll; \ 1109 r1 += r0; \ 1110 r0 = 0x100000000 ll; \ 1111 l1_%=: r0 += 1; \ 1112 if r0 < r1 goto l1_%=; \ 1113 l0_%=: r0 = 0; \ 1114 exit; \ 1115 " : 1116 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1117 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1118 : __clobber_all); 1119 } 1120 1121 SEC("xdp") 1122 __description("bound check with JMP32_JLT for crossing 32-bit signed boundary") 1123 __success __retval(0) 1124 __naked void crossing_32_bit_signed_boundary_1(void) 1125 { 1126 asm volatile (" \ 1127 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1128 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1129 r1 = r2; \ 1130 r1 += 1; \ 1131 if r1 > r3 goto l0_%=; \ 1132 r1 = *(u8*)(r2 + 0); \ 1133 w0 = 0x7fffff10; \ 1134 w1 += w0; \ 1135 w0 = 0x80000000; \ 1136 l1_%=: w0 += 1; \ 1137 /* r1 unsigned range is [0, 0x8000000f] */ \ 1138 if w0 < w1 goto l1_%=; \ 1139 l0_%=: r0 = 0; \ 1140 exit; \ 1141 " : 1142 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1143 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1144 : __clobber_all); 1145 } 1146 1147 SEC("xdp") 1148 __description("bound check with JMP32_JSLT for crossing 32-bit signed boundary") 1149 __success __retval(0) 1150 __naked void crossing_32_bit_signed_boundary_2(void) 1151 { 1152 asm volatile (" \ 1153 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1154 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1155 r1 = r2; \ 1156 r1 += 1; \ 1157 if r1 > r3 goto l0_%=; \ 1158 r1 = *(u8*)(r2 + 0); \ 1159 w0 = 0x7fffff10; \ 1160 w1 += w0; \ 1161 w2 = 0x80000fff; \ 1162 w0 = 0x80000000; \ 1163 l1_%=: w0 += 1; \ 1164 if w0 s> w2 goto l0_%=; \ 1165 /* r1 signed range is [S32_MIN, S32_MAX] */ \ 1166 if w0 s< w1 goto l1_%=; \ 1167 r0 = 1; \ 1168 exit; \ 1169 l0_%=: r0 = 0; \ 1170 exit; \ 1171 " : 1172 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1173 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1174 : __clobber_all); 1175 } 1176 1177 SEC("tc") 1178 __description("bounds check with JMP_NE for reg edge") 1179 __success __retval(0) 1180 __naked void reg_not_equal_const(void) 1181 { 1182 asm volatile (" \ 1183 r6 = r1; \ 1184 r1 = 0; \ 1185 *(u64*)(r10 - 8) = r1; \ 1186 call %[bpf_get_prandom_u32]; \ 1187 r4 = r0; \ 1188 r4 &= 7; \ 1189 if r4 != 0 goto l0_%=; \ 1190 r0 = 0; \ 1191 exit; \ 1192 l0_%=: r1 = r6; \ 1193 r2 = 0; \ 1194 r3 = r10; \ 1195 r3 += -8; \ 1196 r5 = 0; \ 1197 /* The 4th argument of bpf_skb_store_bytes is defined as \ 1198 * ARG_CONST_SIZE, so 0 is not allowed. The 'r4 != 0' \ 1199 * is providing us this exclusion of zero from initial \ 1200 * [0, 7] range. \ 1201 */ \ 1202 call %[bpf_skb_store_bytes]; \ 1203 r0 = 0; \ 1204 exit; \ 1205 " : 1206 : __imm(bpf_get_prandom_u32), 1207 __imm(bpf_skb_store_bytes) 1208 : __clobber_all); 1209 } 1210 1211 SEC("tc") 1212 __description("bounds check with JMP_EQ for reg edge") 1213 __success __retval(0) 1214 __naked void reg_equal_const(void) 1215 { 1216 asm volatile (" \ 1217 r6 = r1; \ 1218 r1 = 0; \ 1219 *(u64*)(r10 - 8) = r1; \ 1220 call %[bpf_get_prandom_u32]; \ 1221 r4 = r0; \ 1222 r4 &= 7; \ 1223 if r4 == 0 goto l0_%=; \ 1224 r1 = r6; \ 1225 r2 = 0; \ 1226 r3 = r10; \ 1227 r3 += -8; \ 1228 r5 = 0; \ 1229 /* Just the same as what we do in reg_not_equal_const() */ \ 1230 call %[bpf_skb_store_bytes]; \ 1231 l0_%=: r0 = 0; \ 1232 exit; \ 1233 " : 1234 : __imm(bpf_get_prandom_u32), 1235 __imm(bpf_skb_store_bytes) 1236 : __clobber_all); 1237 } 1238 1239 SEC("tc") 1240 __description("multiply mixed sign bounds. test 1") 1241 __success __log_level(2) 1242 __msg("r6 *= r7 {{.*}}; R6=scalar(smin=umin=0x1bc16d5cd4927ee1,smax=umax=0x1bc16d674ec80000,smax32=0x7ffffeff,var_off=(0x1bc16d4000000000; 0x3ffffffeff))") 1243 /* cnum can't represent both [0, 0xffff_feff] and [0x8000_0000, 0x7fff_feff], so it picks one */ 1244 __naked void mult_mixed0_sign(void) 1245 { 1246 asm volatile ( 1247 "call %[bpf_get_prandom_u32];" 1248 "r6 = r0;" 1249 "call %[bpf_get_prandom_u32];" 1250 "r7 = r0;" 1251 "r6 &= 0xf;" 1252 "r6 -= 1000000000;" 1253 "r7 &= 0xf;" 1254 "r7 -= 2000000000;" 1255 "r6 *= r7;" 1256 "exit" 1257 : 1258 : __imm(bpf_get_prandom_u32), 1259 __imm(bpf_skb_store_bytes) 1260 : __clobber_all); 1261 } 1262 1263 SEC("tc") 1264 __description("multiply mixed sign bounds. test 2") 1265 __success __log_level(2) 1266 __msg("r6 *= r7 {{.*}}; R6=scalar(smin=smin32=-100,smax=smax32=200)") 1267 __naked void mult_mixed1_sign(void) 1268 { 1269 asm volatile ( 1270 "call %[bpf_get_prandom_u32];" 1271 "r6 = r0;" 1272 "call %[bpf_get_prandom_u32];" 1273 "r7 = r0;" 1274 "r6 &= 0xf;" 1275 "r6 -= 0xa;" 1276 "r7 &= 0xf;" 1277 "r7 -= 0x14;" 1278 "r6 *= r7;" 1279 "exit" 1280 : 1281 : __imm(bpf_get_prandom_u32), 1282 __imm(bpf_skb_store_bytes) 1283 : __clobber_all); 1284 } 1285 1286 SEC("tc") 1287 __description("multiply negative bounds") 1288 __success __log_level(2) 1289 __msg("r6 *= r7 {{.*}}; R6=scalar(smin=umin=smin32=umin32=0x3ff280b0,smax=umax=smax32=umax32=0x3fff0001,var_off=(0x3ff00000; 0xf81ff))") 1290 __naked void mult_sign_bounds(void) 1291 { 1292 asm volatile ( 1293 "r8 = 0x7fff;" 1294 "call %[bpf_get_prandom_u32];" 1295 "r6 = r0;" 1296 "call %[bpf_get_prandom_u32];" 1297 "r7 = r0;" 1298 "r6 &= 0xa;" 1299 "r6 -= r8;" 1300 "r7 &= 0xf;" 1301 "r7 -= r8;" 1302 "r6 *= r7;" 1303 "exit" 1304 : 1305 : __imm(bpf_get_prandom_u32), 1306 __imm(bpf_skb_store_bytes) 1307 : __clobber_all); 1308 } 1309 1310 SEC("tc") 1311 __description("multiply bounds that don't cross signed boundary") 1312 __success __log_level(2) 1313 __msg("r8 *= r6 {{.*}}; R6=scalar(smin=smin32=0,smax=umax=smax32=umax32=11,var_off=(0x0; 0xb)) R8=scalar(smin=0,smax=umax=0x7b96bb0a94a3a7cd,var_off=(0x0; 0x7fffffffffffffff))") 1314 __naked void mult_no_sign_crossing(void) 1315 { 1316 asm volatile ( 1317 "r6 = 0xb;" 1318 "r8 = 0xb3c3f8c99262687 ll;" 1319 "call %[bpf_get_prandom_u32];" 1320 "r7 = r0;" 1321 "r6 &= r7;" 1322 "r8 *= r6;" 1323 "exit" 1324 : 1325 : __imm(bpf_get_prandom_u32), 1326 __imm(bpf_skb_store_bytes) 1327 : __clobber_all); 1328 } 1329 1330 SEC("tc") 1331 __description("multiplication overflow, result in unbounded reg. test 1") 1332 __success __log_level(2) 1333 __msg("r6 *= r7 {{.*}}; R6=scalar()") 1334 __naked void mult_unsign_ovf(void) 1335 { 1336 asm volatile ( 1337 "r8 = 0x7ffffffffff ll;" 1338 "call %[bpf_get_prandom_u32];" 1339 "r6 = r0;" 1340 "call %[bpf_get_prandom_u32];" 1341 "r7 = r0;" 1342 "r6 &= 0x7fffffff;" 1343 "r7 &= r8;" 1344 "r6 *= r7;" 1345 "exit" 1346 : 1347 : __imm(bpf_get_prandom_u32), 1348 __imm(bpf_skb_store_bytes) 1349 : __clobber_all); 1350 } 1351 1352 SEC("tc") 1353 __description("multiplication overflow, result in unbounded reg. test 2") 1354 __success __log_level(2) 1355 __msg("r6 *= r7 {{.*}}; R6=scalar()") 1356 __naked void mult_sign_ovf(void) 1357 { 1358 asm volatile ( 1359 "r8 = 0x7ffffffff ll;" 1360 "call %[bpf_get_prandom_u32];" 1361 "r6 = r0;" 1362 "call %[bpf_get_prandom_u32];" 1363 "r7 = r0;" 1364 "r6 &= 0xa;" 1365 "r6 -= r8;" 1366 "r7 &= 0x7fffffff;" 1367 "r6 *= r7;" 1368 "exit" 1369 : 1370 : __imm(bpf_get_prandom_u32), 1371 __imm(bpf_skb_store_bytes) 1372 : __clobber_all); 1373 } 1374 1375 SEC("socket") 1376 __description("64-bit addition, all outcomes overflow") 1377 __success __log_level(2) 1378 __msg("5: (0f) r3 += r3 {{.*}} R3=scalar(umin=0x4000000000000000,umax=0xfffffffffffffffe)") 1379 __retval(0) 1380 __naked void add64_full_overflow(void) 1381 { 1382 asm volatile ( 1383 "call %[bpf_get_prandom_u32];" 1384 "r4 = r0;" 1385 "r3 = 0xa000000000000000 ll;" 1386 "r3 |= r4;" 1387 "r3 += r3;" 1388 "r0 = 0;" 1389 "exit" 1390 : 1391 : __imm(bpf_get_prandom_u32) 1392 : __clobber_all); 1393 } 1394 1395 SEC("socket") 1396 __description("64-bit addition, partial overflow, result in unbounded reg") 1397 __success __log_level(2) 1398 __msg("4: (0f) r3 += r3 {{.*}} R3=scalar()") 1399 __retval(0) 1400 __naked void add64_partial_overflow(void) 1401 { 1402 asm volatile ( 1403 "call %[bpf_get_prandom_u32];" 1404 "r4 = r0;" 1405 "r3 = 2;" 1406 "r3 |= r4;" 1407 "r3 += r3;" 1408 "r0 = 0;" 1409 "exit" 1410 : 1411 : __imm(bpf_get_prandom_u32) 1412 : __clobber_all); 1413 } 1414 1415 SEC("socket") 1416 __description("32-bit addition overflow, all outcomes overflow") 1417 __success __log_level(2) 1418 __msg("4: (0c) w3 += w3 {{.*}} R3=scalar(smin=umin=umin32=0x40000000,smax=umax=umax32=0xfffffffe,var_off=(0x0; 0xffffffff))") 1419 __retval(0) 1420 __naked void add32_full_overflow(void) 1421 { 1422 asm volatile ( 1423 "call %[bpf_get_prandom_u32];" 1424 "w4 = w0;" 1425 "w3 = 0xa0000000;" 1426 "w3 |= w4;" 1427 "w3 += w3;" 1428 "r0 = 0;" 1429 "exit" 1430 : 1431 : __imm(bpf_get_prandom_u32) 1432 : __clobber_all); 1433 } 1434 1435 SEC("socket") 1436 __description("32-bit addition, partial overflow, result in unbounded u32 bounds") 1437 __success __log_level(2) 1438 __msg("4: (0c) w3 += w3 {{.*}} R3=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))") 1439 __retval(0) 1440 __naked void add32_partial_overflow(void) 1441 { 1442 asm volatile ( 1443 "call %[bpf_get_prandom_u32];" 1444 "w4 = w0;" 1445 "w3 = 2;" 1446 "w3 |= w4;" 1447 "w3 += w3;" 1448 "r0 = 0;" 1449 "exit" 1450 : 1451 : __imm(bpf_get_prandom_u32) 1452 : __clobber_all); 1453 } 1454 1455 SEC("socket") 1456 __description("64-bit subtraction, all outcomes underflow") 1457 __success __log_level(2) 1458 __msg("6: (1f) r3 -= r1 {{.*}} R3=scalar(umin=1,umax=0x8000000000000000)") 1459 __retval(0) 1460 __naked void sub64_full_overflow(void) 1461 { 1462 asm volatile ( 1463 "call %[bpf_get_prandom_u32];" 1464 "r1 = r0;" 1465 "r2 = 0x8000000000000000 ll;" 1466 "r1 |= r2;" 1467 "r3 = 0;" 1468 "r3 -= r1;" 1469 "r0 = 0;" 1470 "exit" 1471 : 1472 : __imm(bpf_get_prandom_u32) 1473 : __clobber_all); 1474 } 1475 1476 SEC("socket") 1477 __description("64-bit subtraction, partial overflow, result in unbounded reg") 1478 __success __log_level(2) 1479 __msg("3: (1f) r3 -= r2 {{.*}} R3=scalar(id=1-1)") 1480 __retval(0) 1481 __naked void sub64_partial_overflow(void) 1482 { 1483 asm volatile ( 1484 "call %[bpf_get_prandom_u32];" 1485 "r3 = r0;" 1486 "r2 = 1;" 1487 "r3 -= r2;" 1488 "r0 = 0;" 1489 "exit" 1490 : 1491 : __imm(bpf_get_prandom_u32) 1492 : __clobber_all); 1493 } 1494 1495 SEC("socket") 1496 __description("32-bit subtraction overflow, all outcomes underflow") 1497 __success __log_level(2) 1498 __msg("5: (1c) w3 -= w1 {{.*}} R3=scalar(smin=umin=umin32=1,smax=umax=umax32=0x80000000,var_off=(0x0; 0xffffffff))") 1499 __retval(0) 1500 __naked void sub32_full_overflow(void) 1501 { 1502 asm volatile ( 1503 "call %[bpf_get_prandom_u32];" 1504 "w1 = w0;" 1505 "w2 = 0x80000000;" 1506 "w1 |= w2;" 1507 "w3 = 0;" 1508 "w3 -= w1;" 1509 "r0 = 0;" 1510 "exit" 1511 : 1512 : __imm(bpf_get_prandom_u32) 1513 : __clobber_all); 1514 } 1515 1516 SEC("socket") 1517 __description("32-bit subtraction, partial overflow, result in unbounded u32 bounds") 1518 __success __log_level(2) 1519 __msg("3: (1c) w3 -= w2 {{.*}} R3=scalar(smin=0,smax=umax=0xffffffff,var_off=(0x0; 0xffffffff))") 1520 __retval(0) 1521 __naked void sub32_partial_overflow(void) 1522 { 1523 asm volatile ( 1524 "call %[bpf_get_prandom_u32];" 1525 "w3 = w0;" 1526 "w2 = 1;" 1527 "w3 -= w2;" 1528 "r0 = 0;" 1529 "exit" 1530 : 1531 : __imm(bpf_get_prandom_u32) 1532 : __clobber_all); 1533 } 1534 1535 SEC("socket") 1536 __description("dead branch on jset, does not result in invariants violation error") 1537 __success __log_level(2) 1538 __retval(0) 1539 __naked void jset_range_analysis(void) 1540 { 1541 asm volatile (" \ 1542 call %[bpf_get_netns_cookie]; \ 1543 if r0 == 0 goto l0_%=; \ 1544 if r0 & 0xffffffff goto +0; \ 1545 l0_%=: r0 = 0; \ 1546 exit; \ 1547 " : 1548 : __imm(bpf_get_netns_cookie) 1549 : __clobber_all); 1550 } 1551 1552 /* This test covers the bounds deduction on 64bits when the s64 and u64 ranges 1553 * overlap on the negative side. At instruction 7, the ranges look as follows: 1554 * 1555 * 0 umin=0xfffffcf1 umax=0xff..ff6e U64_MAX 1556 * | [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | 1557 * |----------------------------|------------------------------| 1558 * |xxxxxxxxxx] [xxxxxxxxxxxx| 1559 * 0 smax=0xeffffeee smin=-655 -1 1560 * 1561 * We should therefore deduce the following new bounds: 1562 * 1563 * 0 u64=[0xff..ffd71;0xff..ff6e] U64_MAX 1564 * | [xxx] | 1565 * |----------------------------|------------------------------| 1566 * | [xxx] | 1567 * 0 s64=[-655;-146] -1 1568 * 1569 * Without the deduction cross sign boundary, we end up with an invariant 1570 * violation error. 1571 */ 1572 SEC("socket") 1573 __description("bounds deduction cross sign boundary, negative overlap") 1574 __success __log_level(2) 1575 __msg("7: (1f) r0 -= r6 {{.*}} R0=scalar(smin=smin32=-655,smax=smax32=-146,umin=0xfffffffffffffd71,umax=0xffffffffffffff6e,umin32=0xfffffd71,umax32=0xffffff6e,var_off=(0xfffffffffffffc00; 0x3ff))") 1576 __retval(0) 1577 __naked void bounds_deduct_negative_overlap(void) 1578 { 1579 asm volatile(" \ 1580 call %[bpf_get_prandom_u32]; \ 1581 w3 = w0; \ 1582 w6 = (s8)w0; \ 1583 r0 = (s8)r0; \ 1584 if w6 >= 0xf0000000 goto l0_%=; \ 1585 r0 += r6; \ 1586 r6 += 400; \ 1587 r0 -= r6; \ 1588 if r3 < r0 goto l0_%=; \ 1589 l0_%=: r0 = 0; \ 1590 exit; \ 1591 " : 1592 : __imm(bpf_get_prandom_u32) 1593 : __clobber_all); 1594 } 1595 1596 /* This test covers the bounds deduction on 64bits when the s64 and u64 ranges 1597 * overlap on the positive side. At instruction 3, the ranges look as follows: 1598 * 1599 * 0 umin=0 umax=0xffffffffffffff00 U64_MAX 1600 * [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | 1601 * |----------------------------|------------------------------| 1602 * |xxxxxxxx] [xxxxxxxx| 1603 * 0 smax=127 smin=-128 -1 1604 * 1605 * We should therefore deduce the following new bounds: 1606 * 1607 * 0 u64=[0;127] U64_MAX 1608 * [xxxxxxxx] | 1609 * |----------------------------|------------------------------| 1610 * [xxxxxxxx] | 1611 * 0 s64=[0;127] -1 1612 * 1613 * Without the deduction cross sign boundary, the program is rejected due to 1614 * the frame pointer write. 1615 */ 1616 SEC("socket") 1617 __description("bounds deduction cross sign boundary, positive overlap") 1618 __success __log_level(2) 1619 __msg("3: (2d) if r0 > r1 {{.*}} R0=scalar(smin=smin32=0,smax=umax=smax32=umax32=127,var_off=(0x0; 0x7f))") 1620 __retval(0) 1621 __naked void bounds_deduct_positive_overlap(void) 1622 { 1623 asm volatile(" \ 1624 call %[bpf_get_prandom_u32]; \ 1625 r0 = (s8)r0; \ 1626 r1 = 0xffffffffffffff00; \ 1627 if r0 > r1 goto l0_%=; \ 1628 if r0 < 128 goto l0_%=; \ 1629 r10 = 0; \ 1630 l0_%=: r0 = 0; \ 1631 exit; \ 1632 " : 1633 : __imm(bpf_get_prandom_u32) 1634 : __clobber_all); 1635 } 1636 1637 /* This test is the same as above, but the s64 and u64 ranges overlap in two 1638 * places. At instruction 3, the ranges look as follows: 1639 * 1640 * 0 umin=0 umax=0xffffffffffffff80 U64_MAX 1641 * [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | 1642 * |----------------------------|------------------------------| 1643 * |xxxxxxxx] [xxxxxxxx| 1644 * 0 smax=127 smin=-128 -1 1645 * 1646 * 0xffffffffffffff80 = (u64)-128. We therefore can't deduce anything new and 1647 * the program should fail due to the frame pointer write. 1648 */ 1649 SEC("socket") 1650 __description("bounds deduction cross sign boundary, two overlaps") 1651 __failure 1652 __msg("3: (2d) if r0 > r1 {{.*}} R0=scalar(smin=smin32=-128,smax=smax32=127)") 1653 /* smin=-128 includes point 0xffffffffffffff80 */ 1654 __msg("frame pointer is read only") 1655 __naked void bounds_deduct_two_overlaps(void) 1656 { 1657 asm volatile(" \ 1658 call %[bpf_get_prandom_u32]; \ 1659 r0 = (s8)r0; \ 1660 r1 = 0xffffffffffffff80; \ 1661 if r0 > r1 goto l0_%=; \ 1662 if r0 < 128 goto l0_%=; \ 1663 r10 = 0; \ 1664 l0_%=: r0 = 0; \ 1665 exit; \ 1666 " : 1667 : __imm(bpf_get_prandom_u32) 1668 : __clobber_all); 1669 } 1670 1671 SEC("socket") 1672 __description("dead jne branch due to disagreeing tnums") 1673 __success __log_level(2) 1674 __naked void jne_disagreeing_tnums(void *ctx) 1675 { 1676 asm volatile(" \ 1677 call %[bpf_get_prandom_u32]; \ 1678 w0 = w0; \ 1679 r0 >>= 30; \ 1680 r0 <<= 30; \ 1681 r1 = r0; \ 1682 r1 += 1024; \ 1683 if r1 != r0 goto +1; \ 1684 r10 = 0; \ 1685 exit; \ 1686 " : 1687 : __imm(bpf_get_prandom_u32) 1688 : __clobber_all); 1689 } 1690 1691 SEC("socket") 1692 __description("dead jeq branch due to disagreeing tnums") 1693 __success __log_level(2) 1694 __naked void jeq_disagreeing_tnums(void *ctx) 1695 { 1696 asm volatile(" \ 1697 call %[bpf_get_prandom_u32]; \ 1698 w0 = w0; \ 1699 r0 >>= 30; \ 1700 r0 <<= 30; \ 1701 r1 = r0; \ 1702 r1 += 1024; \ 1703 if r1 == r0 goto +1; \ 1704 exit; \ 1705 r10 = 0; \ 1706 exit; \ 1707 " : 1708 : __imm(bpf_get_prandom_u32) 1709 : __clobber_all); 1710 } 1711 1712 SEC("socket") 1713 __description("conditional jump on same register, branch taken") 1714 __not_msg("20: (b7) r0 = 1 {{.*}} R0=1") 1715 __success __log_level(2) 1716 __retval(0) 1717 __naked void condition_jump_on_same_register(void *ctx) 1718 { 1719 asm volatile(" \ 1720 call %[bpf_get_prandom_u32]; \ 1721 w8 = 0x80000000; \ 1722 r0 &= r8; \ 1723 if r0 == r0 goto +1; \ 1724 goto l1_%=; \ 1725 if r0 >= r0 goto +1; \ 1726 goto l1_%=; \ 1727 if r0 s>= r0 goto +1; \ 1728 goto l1_%=; \ 1729 if r0 <= r0 goto +1; \ 1730 goto l1_%=; \ 1731 if r0 s<= r0 goto +1; \ 1732 goto l1_%=; \ 1733 if r0 != r0 goto l1_%=; \ 1734 if r0 > r0 goto l1_%=; \ 1735 if r0 s> r0 goto l1_%=; \ 1736 if r0 < r0 goto l1_%=; \ 1737 if r0 s< r0 goto l1_%=; \ 1738 l0_%=: r0 = 0; \ 1739 exit; \ 1740 l1_%=: r0 = 1; \ 1741 exit; \ 1742 " : 1743 : __imm(bpf_get_prandom_u32) 1744 : __clobber_all); 1745 } 1746 1747 SEC("socket") 1748 __description("jset on same register, constant value branch taken") 1749 __not_msg("7: (b7) r0 = 1 {{.*}} R0=1") 1750 __success __log_level(2) 1751 __retval(0) 1752 __naked void jset_on_same_register_1(void *ctx) 1753 { 1754 asm volatile(" \ 1755 r0 = 0; \ 1756 if r0 & r0 goto l1_%=; \ 1757 r0 = 1; \ 1758 if r0 & r0 goto +1; \ 1759 goto l1_%=; \ 1760 l0_%=: r0 = 0; \ 1761 exit; \ 1762 l1_%=: r0 = 1; \ 1763 exit; \ 1764 " : 1765 : __imm(bpf_get_prandom_u32) 1766 : __clobber_all); 1767 } 1768 1769 SEC("socket") 1770 __description("jset on same register, scalar value branch taken") 1771 __not_msg("12: (b7) r0 = 1 {{.*}} R0=1") 1772 __success __log_level(2) 1773 __retval(0) 1774 __naked void jset_on_same_register_2(void *ctx) 1775 { 1776 asm volatile(" \ 1777 /* range [1;2] */ \ 1778 call %[bpf_get_prandom_u32]; \ 1779 r0 &= 0x1; \ 1780 r0 += 1; \ 1781 if r0 & r0 goto +1; \ 1782 goto l1_%=; \ 1783 /* range [-2;-1] */ \ 1784 call %[bpf_get_prandom_u32]; \ 1785 r0 &= 0x1; \ 1786 r0 -= 2; \ 1787 if r0 & r0 goto +1; \ 1788 goto l1_%=; \ 1789 l0_%=: r0 = 0; \ 1790 exit; \ 1791 l1_%=: r0 = 1; \ 1792 exit; \ 1793 " : 1794 : __imm(bpf_get_prandom_u32) 1795 : __clobber_all); 1796 } 1797 1798 SEC("socket") 1799 __description("jset on same register, scalar value unknown branch 1") 1800 __msg("3: (b7) r0 = 0 {{.*}} R0=0") 1801 __msg("5: (b7) r0 = 1 {{.*}} R0=1") 1802 __success __log_level(2) 1803 __naked void jset_on_same_register_3(void *ctx) 1804 { 1805 asm volatile(" \ 1806 /* range [0;1] */ \ 1807 call %[bpf_get_prandom_u32]; \ 1808 r0 &= 0x1; \ 1809 if r0 & r0 goto l1_%=; \ 1810 l0_%=: r0 = 0; \ 1811 exit; \ 1812 l1_%=: r0 = 1; \ 1813 exit; \ 1814 " : 1815 : __imm(bpf_get_prandom_u32) 1816 : __clobber_all); 1817 } 1818 1819 SEC("socket") 1820 __description("jset on same register, scalar value unknown branch 2") 1821 __msg("4: (b7) r0 = 0 {{.*}} R0=0") 1822 __msg("6: (b7) r0 = 1 {{.*}} R0=1") 1823 __success __log_level(2) 1824 __naked void jset_on_same_register_4(void *ctx) 1825 { 1826 asm volatile(" \ 1827 /* range [-1;0] */ \ 1828 call %[bpf_get_prandom_u32]; \ 1829 r0 &= 0x1; \ 1830 r0 -= 1; \ 1831 if r0 & r0 goto l1_%=; \ 1832 l0_%=: r0 = 0; \ 1833 exit; \ 1834 l1_%=: r0 = 1; \ 1835 exit; \ 1836 " : 1837 : __imm(bpf_get_prandom_u32) 1838 : __clobber_all); 1839 } 1840 1841 SEC("socket") 1842 __description("jset on same register, scalar value unknown branch 3") 1843 __msg("4: (b7) r0 = 0 {{.*}} R0=0") 1844 __msg("6: (b7) r0 = 1 {{.*}} R0=1") 1845 __success __log_level(2) 1846 __naked void jset_on_same_register_5(void *ctx) 1847 { 1848 asm volatile(" \ 1849 /* range [-1;1] */ \ 1850 call %[bpf_get_prandom_u32]; \ 1851 r0 &= 0x2; \ 1852 r0 -= 1; \ 1853 if r0 & r0 goto l1_%=; \ 1854 l0_%=: r0 = 0; \ 1855 exit; \ 1856 l1_%=: r0 = 1; \ 1857 exit; \ 1858 " : 1859 : __imm(bpf_get_prandom_u32) 1860 : __clobber_all); 1861 } 1862 1863 /* This test covers the bounds deduction when the u64 range and the tnum 1864 * overlap only at umax. After instruction 3, the ranges look as follows: 1865 * 1866 * 0 umin=0xe01 umax=0xf00 U64_MAX 1867 * | [xxxxxxxxxxxxxx] | 1868 * |----------------------------|------------------------------| 1869 * | x x | tnum values 1870 * 1871 * The verifier can therefore deduce that the R0=0xf0=240. 1872 */ 1873 SEC("socket") 1874 __description("bounds refinement with single-value tnum on umax") 1875 __msg("3: (15) if r0 == 0xe0 {{.*}} R0=240") 1876 __success __log_level(2) 1877 __naked void bounds_refinement_tnum_umax(void *ctx) 1878 { 1879 asm volatile(" \ 1880 call %[bpf_get_prandom_u32]; \ 1881 r0 |= 0xe0; \ 1882 r0 &= 0xf0; \ 1883 if r0 == 0xe0 goto +2; \ 1884 if r0 == 0xf0 goto +1; \ 1885 r10 = 0; \ 1886 exit; \ 1887 " : 1888 : __imm(bpf_get_prandom_u32) 1889 : __clobber_all); 1890 } 1891 1892 /* This test covers the bounds deduction when the u64 range and the tnum 1893 * overlap only at umin. After instruction 3, the ranges look as follows: 1894 * 1895 * 0 umin=0xe1 umax=0xf0 U64_MAX 1896 * | [xxxxxxxxxxxxxx] | 1897 * |----------------------------|------------------------------| 1898 * | x x | tnum values 1899 * 1900 * The verifier can therefore deduce that the R0=0xe1=225. 1901 */ 1902 SEC("socket") 1903 __description("bounds refinement with single-value tnum on umin") 1904 __msg("3: (15) if r0 == 0xf1 {{.*}} R0=225") 1905 __success __log_level(2) 1906 __naked void bounds_refinement_tnum_umin(void *ctx) 1907 { 1908 asm volatile(" \ 1909 call %[bpf_get_prandom_u32]; \ 1910 r0 |= 0xe1; \ 1911 r0 &= 0xf1; \ 1912 if r0 == 0xf1 goto +2; \ 1913 if r0 == 0xe1 goto +1; \ 1914 r10 = 0; \ 1915 exit; \ 1916 " : 1917 : __imm(bpf_get_prandom_u32) 1918 : __clobber_all); 1919 } 1920 1921 /* This test covers the bounds deduction when the only possible tnum value is 1922 * in the middle of the u64 range. After instruction 3, the ranges look as 1923 * follows: 1924 * 1925 * 0 umin=0x7cf umax=0x7df U64_MAX 1926 * | [xxxxxxxxxxxx] | 1927 * |----------------------------|------------------------------| 1928 * | x x x x x | tnum values 1929 * | +--- 0x7e0 1930 * +--- 0x7d0 1931 * 1932 * Since the lower four bits are zero, the tnum and the u64 range only overlap 1933 * in R0=0x7d0=2000. Instruction 5 is therefore dead code. 1934 */ 1935 SEC("socket") 1936 __description("bounds refinement with single-value tnum in middle of range") 1937 __msg("3: (a5) if r0 < 0x7cf {{.*}} R0=2000") 1938 __success __log_level(2) 1939 __naked void bounds_refinement_tnum_middle(void *ctx) 1940 { 1941 asm volatile(" \ 1942 call %[bpf_get_prandom_u32]; \ 1943 if r0 & 0x0f goto +4; \ 1944 if r0 > 0x7df goto +3; \ 1945 if r0 < 0x7cf goto +2; \ 1946 if r0 == 0x7d0 goto +1; \ 1947 r10 = 0; \ 1948 exit; \ 1949 " : 1950 : __imm(bpf_get_prandom_u32) 1951 : __clobber_all); 1952 } 1953 1954 /* This test cover the negative case for the tnum/u64 overlap. Since 1955 * they contain the same two values (i.e., {0, 1}), we can't deduce 1956 * anything more. 1957 */ 1958 SEC("socket") 1959 __description("bounds refinement: several overlaps between tnum and u64") 1960 __msg("2: (25) if r0 > 0x1 {{.*}} R0=scalar(smin=smin32=0,smax=umax=smax32=umax32=1,var_off=(0x0; 0x1))") 1961 __failure __log_level(2) 1962 __naked void bounds_refinement_several_overlaps(void *ctx) 1963 { 1964 asm volatile(" \ 1965 call %[bpf_get_prandom_u32]; \ 1966 if r0 < 0 goto +3; \ 1967 if r0 > 1 goto +2; \ 1968 if r0 == 1 goto +1; \ 1969 r10 = 0; \ 1970 exit; \ 1971 " : 1972 : __imm(bpf_get_prandom_u32) 1973 : __clobber_all); 1974 } 1975 1976 /* This test cover the negative case for the tnum/u64 overlap. Since 1977 * they overlap in the two values contained by the u64 range (i.e., 1978 * {0xf, 0x10}), we can't deduce anything more. 1979 */ 1980 SEC("socket") 1981 __description("bounds refinement: multiple overlaps between tnum and u64") 1982 __msg("2: (25) if r0 > 0x10 {{.*}} R0=scalar(smin=umin=smin32=umin32=15,smax=umax=smax32=umax32=16,var_off=(0x0; 0x1f))") 1983 __failure __log_level(2) 1984 __naked void bounds_refinement_multiple_overlaps(void *ctx) 1985 { 1986 asm volatile(" \ 1987 call %[bpf_get_prandom_u32]; \ 1988 if r0 < 0xf goto +3; \ 1989 if r0 > 0x10 goto +2; \ 1990 if r0 == 0x10 goto +1; \ 1991 r10 = 0; \ 1992 exit; \ 1993 " : 1994 : __imm(bpf_get_prandom_u32) 1995 : __clobber_all); 1996 } 1997 1998 SEC("socket") 1999 __success 2000 __naked void signed_unsigned_intersection32_case1(void *ctx) 2001 { 2002 asm volatile(" \ 2003 call %[bpf_get_prandom_u32]; \ 2004 w0 &= 0xffffffff; \ 2005 if w0 < 0x3 goto 1f; /* on fall-through u32 range [3..U32_MAX] */ \ 2006 if w0 s> 0x1 goto 1f; /* on fall-through s32 range [S32_MIN..1] */ \ 2007 if w0 s< 0x0 goto 1f; /* range can be narrowed to [S32_MIN..-1] */ \ 2008 r10 = 0; /* thus predicting the jump. */ \ 2009 1: exit; \ 2010 " : 2011 : __imm(bpf_get_prandom_u32) 2012 : __clobber_all); 2013 } 2014 2015 SEC("socket") 2016 __success 2017 __naked void signed_unsigned_intersection32_case2(void *ctx) 2018 { 2019 asm volatile(" \ 2020 call %[bpf_get_prandom_u32]; \ 2021 w0 &= 0xffffffff; \ 2022 if w0 > 0x80000003 goto 1f; /* on fall-through u32 range [0..S32_MIN+3] */ \ 2023 if w0 s< -3 goto 1f; /* on fall-through s32 range [-3..S32_MAX] */ \ 2024 if w0 s> 5 goto 1f; /* on fall-through s32 range [-3..5] */ \ 2025 if w0 <= 5 goto 1f; /* range can be narrowed to [0..5] */ \ 2026 r10 = 0; /* thus predicting the jump */ \ 2027 1: exit; \ 2028 " : 2029 : __imm(bpf_get_prandom_u32) 2030 : __clobber_all); 2031 } 2032 2033 /* 2034 * After instruction 3, the u64 and s64 ranges look as follows: 2035 * 0 umin=2 umax=0xff..ff00..03 U64_MAX 2036 * | [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] | 2037 * |----------------------------|------------------------------| 2038 * |xx] [xxxxxxxxxxxxxxxxxxxxxxxxxxxx| 2039 * 0 smax=2 smin=0x800..02 -1 2040 * 2041 * The two ranges can't be refined because they overlap in two places. Once we 2042 * add an upper-bound to u64 at instruction 4, the refinement can happen. This 2043 * test validates that this refinement does happen and is not overwritten by 2044 * the less-precise 32bits ranges. 2045 */ 2046 SEC("socket") 2047 __description("bounds refinement: 64bits ranges not overwritten by 32bits ranges") 2048 __msg("3: (65) if r0 s> 0x2 {{.*}} R0=scalar(smin=0x8000000000000002,smax=2,smin32=umin32=2,smax32=umax32=3,var_off{{.*}}))") 2049 /* Can't represent both [S64_MIN+2, 2] and [2, U64_MAX - U32_MAX + 2] at the same time, picks shorter interval */ 2050 __msg("4: (25) if r0 > 0x13 {{.*}} R0=2") 2051 __success __log_level(2) 2052 __naked void refinement_32bounds_not_overwriting_64bounds(void *ctx) 2053 { 2054 asm volatile(" \ 2055 call %[bpf_get_prandom_u32]; \ 2056 if w0 < 2 goto +5; \ 2057 if w0 > 3 goto +4; \ 2058 if r0 s> 2 goto +3; \ 2059 if r0 > 19 goto +2; \ 2060 if r0 == 2 goto +1; \ 2061 r10 = 0; \ 2062 exit; \ 2063 " : 2064 : __imm(bpf_get_prandom_u32) 2065 : __clobber_all); 2066 } 2067 2068 SEC("socket") 2069 __description("maybe_fork_scalars: OR with constant rejects OOB") 2070 __failure __msg("invalid access to map value") 2071 __naked void or_scalar_fork_rejects_oob(void) 2072 { 2073 asm volatile (" \ 2074 r1 = 0; \ 2075 *(u64*)(r10 - 8) = r1; \ 2076 r2 = r10; \ 2077 r2 += -8; \ 2078 r1 = %[map_hash_8b] ll; \ 2079 call %[bpf_map_lookup_elem]; \ 2080 if r0 == 0 goto l0_%=; \ 2081 r9 = r0; \ 2082 r6 = *(u64*)(r9 + 0); \ 2083 r6 s>>= 63; \ 2084 r6 |= 8; \ 2085 /* r6 is -1 (current) or 8 (pushed) */ \ 2086 if r6 s< 0 goto l0_%=; \ 2087 /* pushed path: r6 = 8, OOB for value_size=8 */ \ 2088 r9 += r6; \ 2089 r0 = *(u8*)(r9 + 0); \ 2090 l0_%=: r0 = 0; \ 2091 exit; \ 2092 " : 2093 : __imm(bpf_map_lookup_elem), 2094 __imm_addr(map_hash_8b) 2095 : __clobber_all); 2096 } 2097 2098 SEC("socket") 2099 __description("maybe_fork_scalars: AND with constant still works") 2100 __success __retval(0) 2101 __naked void and_scalar_fork_still_works(void) 2102 { 2103 asm volatile (" \ 2104 r1 = 0; \ 2105 *(u64*)(r10 - 8) = r1; \ 2106 r2 = r10; \ 2107 r2 += -8; \ 2108 r1 = %[map_hash_8b] ll; \ 2109 call %[bpf_map_lookup_elem]; \ 2110 if r0 == 0 goto l0_%=; \ 2111 r9 = r0; \ 2112 r6 = *(u64*)(r9 + 0); \ 2113 r6 s>>= 63; \ 2114 r6 &= 4; \ 2115 /* \ 2116 * r6 is 0 (pushed, 0&4==0) or 4 (current) \ 2117 * both within value_size=8 \ 2118 */ \ 2119 if r6 s< 0 goto l0_%=; \ 2120 r9 += r6; \ 2121 r0 = *(u8*)(r9 + 0); \ 2122 l0_%=: r0 = 0; \ 2123 exit; \ 2124 " : 2125 : __imm(bpf_map_lookup_elem), 2126 __imm_addr(map_hash_8b) 2127 : __clobber_all); 2128 } 2129 2130 SEC("socket") 2131 __description("maybe_fork_scalars: OR with constant allows in-bounds") 2132 __success __retval(0) 2133 __naked void or_scalar_fork_allows_inbounds(void) 2134 { 2135 asm volatile (" \ 2136 r1 = 0; \ 2137 *(u64*)(r10 - 8) = r1; \ 2138 r2 = r10; \ 2139 r2 += -8; \ 2140 r1 = %[map_hash_8b] ll; \ 2141 call %[bpf_map_lookup_elem]; \ 2142 if r0 == 0 goto l0_%=; \ 2143 r9 = r0; \ 2144 r6 = *(u64*)(r9 + 0); \ 2145 r6 s>>= 63; \ 2146 r6 |= 4; \ 2147 /* \ 2148 * r6 is -1 (current) or 4 (pushed) \ 2149 * pushed path: r6 = 4, within value_size=8 \ 2150 */ \ 2151 if r6 s< 0 goto l0_%=; \ 2152 r9 += r6; \ 2153 r0 = *(u8*)(r9 + 0); \ 2154 l0_%=: r0 = 0; \ 2155 exit; \ 2156 " : 2157 : __imm(bpf_map_lookup_elem), 2158 __imm_addr(map_hash_8b) 2159 : __clobber_all); 2160 } 2161 2162 /* 2163 * Last jump can be detected as always taken because the intersection of R5 and 2164 * R7 32bit tnums produces a constant that isn't within R7's s32 bounds. 2165 */ 2166 SEC("socket") 2167 __description("dead branch: tnums give impossible constant if equal") 2168 __success 2169 __naked void tnums_equal_impossible_constant(void *ctx) 2170 { 2171 asm volatile(" \ 2172 call %[bpf_get_prandom_u32]; \ 2173 r5 = r0; \ 2174 /* Set r5's var_off32 to (0; 0xfffffffc) */ \ 2175 r5 &= 0xfffffffffffffffc; \ 2176 r7 = r0; \ 2177 /* Set r7's var_off32 to (0x0; 0x1) */ \ 2178 r7 &= 0x1; \ 2179 /* Now, s32=[-43; -42], var_off32=(0xffffffd4; 0x3) */ \ 2180 r7 += -43; \ 2181 /* On fallthrough, var_off32=-44, not in s32 */ \ 2182 if w5 != w7 goto +1; \ 2183 r10 = 0; \ 2184 exit; \ 2185 " : 2186 : __imm(bpf_get_prandom_u32) 2187 : __clobber_all); 2188 } 2189 2190 /* 2191 * 32-bit range starts before 64-bit range low bits in each 2^32 block. 2192 * 2193 * N*2^32 (N+1)*2^32 (N+2)*2^32 (N+3)*2^32 2194 * ||----|=====|--|----------||----|=====|-------------||--|-|=====|-------------|| 2195 * |< b >| | |< b >| | |< b >| 2196 * | | | | 2197 * |<---------------+- a -+---------------->| 2198 * | | 2199 * |< t >| refined r0 range 2200 * 2201 * a = u64 [0x1'00000008, 0x3'00000001] 2202 * b = u32 [2, 5] 2203 * t = u64 [0x2'00000002, 0x2'00000005] 2204 */ 2205 SEC("socket") 2206 __success 2207 __flag(BPF_F_TEST_REG_INVARIANTS) 2208 __naked void deduce64_from_32_before_block_start(void) 2209 { 2210 asm volatile (" \ 2211 call %[bpf_get_prandom_u32]; \ 2212 r1 = 0x100000008 ll; \ 2213 if r0 < r1 goto 2f; \ 2214 r1 = 0x300000001 ll; \ 2215 if r0 > r1 goto 2f; /* u64: [0x1'00000008, 0x3'00000001] */ \ 2216 if w0 < 2 goto 2f; \ 2217 if w0 > 5 goto 2f; /* u32: [2, 5] */ \ 2218 r2 = 0x200000002 ll; \ 2219 r3 = 0x200000005 ll; \ 2220 if r0 >= r2 goto 1f; /* should be always true */ \ 2221 r10 = 0; /* dead code */ \ 2222 1: if r0 <= r3 goto 2f; /* should be always true */ \ 2223 r10 = 0; /* dead code */ \ 2224 2: exit; \ 2225 " 2226 :: __imm(bpf_get_prandom_u32) 2227 : __clobber_all); 2228 } 2229 2230 /* 2231 * 32-bit range crossing U32_MAX / 0 boundary. 2232 * 2233 * N*2^32 (N+1)*2^32 (N+2)*2^32 (N+3)*2^32 2234 * ||===|---------|------|===||===|----------------|===||===|---------|------|===|| 2235 * |b >| | |< b||b >| |< b||b >| | |< b| 2236 * | | | | 2237 * |<-----+----------------- a --------------+-------->| 2238 * | | 2239 * |<---------------- t ------------->| refined r0 range 2240 * 2241 * a = u64 [0x1'00000006, 0x2'FFFFFFEF] 2242 * b = s32 [-16, 5] (u32 wrapping [0xFFFFFFF0, 0x00000005]) 2243 * t = u64 [0x1'FFFFFFF0, 0x2'00000005] 2244 */ 2245 SEC("socket") 2246 __success 2247 __flag(BPF_F_TEST_REG_INVARIANTS) 2248 __naked void deduce64_from_32_wrapping_32bit(void) 2249 { 2250 asm volatile (" \ 2251 call %[bpf_get_prandom_u32]; \ 2252 r1 = 0x100000006 ll; \ 2253 if r0 < r1 goto 2f; \ 2254 r1 = 0x2ffffffef ll; \ 2255 if r0 > r1 goto 2f; /* u64: [0x1'00000006, 0x2'FFFFFFEF] */ \ 2256 if w0 s< -16 goto 2f; \ 2257 if w0 s> 5 goto 2f; /* s32: [-16, 5] */ \ 2258 r1 = 0x1fffffff0 ll; \ 2259 r2 = 0x200000005 ll; \ 2260 if r0 >= r1 goto 1f; /* should be always true */ \ 2261 r10 = 0; /* dead code */ \ 2262 1: if r0 <= r2 goto 2f; /* should be always true */ \ 2263 r10 = 0; /* dead code */ \ 2264 2: exit; \ 2265 " 2266 :: __imm(bpf_get_prandom_u32) 2267 : __clobber_all); 2268 } 2269 2270 /* Check that range_within() compares cnum ranges, not min/max projections. */ 2271 SEC("socket") 2272 __failure __msg("div by zero") 2273 __flag(BPF_F_TEST_STATE_FREQ) 2274 __naked void range_within_cnum_cross_both_boundaries(void) 2275 { 2276 asm volatile (" \ 2277 call %[bpf_get_prandom_u32]; \ 2278 r1 = 0x80000020; \ 2279 if r0 > r1 goto 1f; \ 2280 r0 += 0x7FFFFFF0; /* PATH 1 */ \ 2281 goto 2f; \ 2282 1: call %[bpf_get_prandom_u32]; /* PATH 2 */ \ 2283 if r0 < 0x100 goto 3f; \ 2284 if r0 > 0x200 goto 3f; \ 2285 2: /* PATH 1: r0 ∈ [0x7FFFFFF0, U32_MAX] ∪ [0, 0x10] */ \ 2286 /* PATH 2: r0 ∈ [0x100, 0x200] */ \ 2287 if r0 != 0x100 goto 3f; /* True only on PATH 2 */ \ 2288 r0 /= 0; \ 2289 3: exit; \ 2290 " 2291 :: __imm(bpf_map_lookup_elem), 2292 __imm_addr(map_hash_8b), 2293 __imm(bpf_get_prandom_u32) 2294 : __clobber_all); 2295 } 2296 2297 char _license[] SEC("license") = "GPL"; 2298