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 8struct { 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 15SEC("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; \ 38l0_%=: r0 = 0; \ 39 exit; \ 40" : 41 : __imm(bpf_map_lookup_elem), 42 __imm_addr(map_hash_8b) 43 : __clobber_all); 44} 45 46SEC("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; \ 69l0_%=: r0 = 0; \ 70 exit; \ 71" : 72 : __imm(bpf_map_lookup_elem), 73 __imm_addr(map_hash_8b) 74 : __clobber_all); 75} 76 77SEC("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; \ 101l0_%=: *(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 110SEC("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); \ 131l0_%=: /* exit */ \ 132 r0 = 0; \ 133 exit; \ 134" : 135 : __imm(bpf_map_lookup_elem), 136 __imm_addr(map_hash_8b) 137 : __clobber_all); 138} 139 140SEC("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); \ 162l0_%=: /* exit */ \ 163 r0 = 0; \ 164 exit; \ 165" : 166 : __imm(bpf_map_lookup_elem), 167 __imm_addr(map_hash_8b) 168 : __clobber_all); 169} 170 171SEC("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); \ 193l0_%=: /* exit */ \ 194 r0 = 0; \ 195 exit; \ 196" : 197 : __imm(bpf_map_lookup_elem), 198 __imm_addr(map_hash_8b) 199 : __clobber_all); 200} 201 202SEC("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]; \ 220l0_%=: 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 231SEC("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]; \ 249l0_%=: 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 261SEC("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); \ 291l0_%=: /* exit */ \ 292 r0 = 0; \ 293 exit; \ 294" : 295 : __imm(bpf_map_lookup_elem), 296 __imm_addr(map_hash_8b) 297 : __clobber_all); 298} 299 300SEC("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; \ 334l0_%=: 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 342SEC("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; \ 376l0_%=: 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 384SEC("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); \ 407l0_%=: /* exit */ \ 408 r0 = 0; \ 409 exit; \ 410" : 411 : __imm(bpf_map_lookup_elem), 412 __imm_addr(map_hash_8b) 413 : __clobber_all); 414} 415 416SEC("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); \ 440l0_%=: /* exit */ \ 441 r0 = 0; \ 442 exit; \ 443" : 444 : __imm(bpf_map_lookup_elem), 445 __imm_addr(map_hash_8b) 446 : __clobber_all); 447} 448 449SEC("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); \ 475l0_%=: /* exit */ \ 476 r0 = 0; \ 477 exit; \ 478" : 479 : __imm(bpf_map_lookup_elem), 480 __imm_addr(map_hash_8b) 481 : __clobber_all); 482} 483 484SEC("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; \ 509l0_%=: exit; \ 510" : 511 : __imm(bpf_map_lookup_elem), 512 __imm_addr(map_hash_8b) 513 : __clobber_all); 514} 515 516SEC("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; \ 531l0_%=: r0 += 0x7ffffffe; \ 532 r0 = *(u64*)(r0 + 0); \ 533 goto l1_%=; \ 534l1_%=: exit; \ 535" : 536 : __imm(bpf_map_lookup_elem), 537 __imm_addr(map_hash_8b) 538 : __clobber_all); 539} 540 541SEC("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; \ 556l0_%=: r0 += 0x1fffffff; \ 557 r0 += 0x1fffffff; \ 558 r0 += 0x1fffffff; \ 559 r0 = *(u64*)(r0 + 0); \ 560 goto l1_%=; \ 561l1_%=: exit; \ 562" : 563 : __imm(bpf_map_lookup_elem), 564 __imm_addr(map_hash_8b) 565 : __clobber_all); 566} 567 568SEC("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; \ 583l0_%=: r0 -= 0x1fffffff; \ 584 r0 -= 0x1fffffff; \ 585 r0 = *(u64*)(r0 + 2); \ 586 goto l1_%=; \ 587l1_%=: exit; \ 588" : 589 : __imm(bpf_map_lookup_elem), 590 __imm_addr(map_hash_8b) 591 : __clobber_all); 592} 593 594SEC("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; \ 609l0_%=: r1 = 1000000; \ 610 r1 *= 1000000; \ 611 r0 += r1; \ 612 r0 = *(u64*)(r0 + 2); \ 613 goto l1_%=; \ 614l1_%=: exit; \ 615" : 616 : __imm(bpf_map_lookup_elem), 617 __imm_addr(map_hash_8b) 618 : __clobber_all); 619} 620 621SEC("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_%=; \ 638l0_%=: /* invalid ldx if bounds are lost above */ \ 639 r0 = *(u64*)(r0 - 1); \ 640l1_%=: exit; \ 641" ::: __clobber_all); 642} 643 644SEC("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_%=; \ 662l0_%=: /* invalid ldx if bounds are lost above */ \ 663 r0 = *(u64*)(r0 - 1); \ 664l1_%=: exit; \ 665" ::: __clobber_all); 666} 667 668SEC("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); \ 684l0_%=: 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 692SEC("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; \ 708l0_%=: r1 = 0; \ 709 r1 ^= 1; \ 710 if r1 != 0 goto l1_%=; \ 711 r0 = *(u64*)(r0 + 8); \ 712l1_%=: r0 = 0; \ 713 exit; \ 714" : 715 : __imm(bpf_map_lookup_elem), 716 __imm_addr(map_hash_8b) 717 : __clobber_all); 718} 719 720SEC("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; \ 736l0_%=: w1 = 0; \ 737 w1 ^= 1; \ 738 if w1 != 0 goto l1_%=; \ 739 r0 = *(u64*)(r0 + 8); \ 740l1_%=: r0 = 0; \ 741 exit; \ 742" : 743 : __imm(bpf_map_lookup_elem), 744 __imm_addr(map_hash_8b) 745 : __clobber_all); 746} 747 748SEC("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; \ 764l0_%=: r1 = 2; \ 765 r1 ^= 3; \ 766 if r1 > 0 goto l1_%=; \ 767 r0 = *(u64*)(r0 + 8); \ 768l1_%=: r0 = 0; \ 769 exit; \ 770" : 771 : __imm(bpf_map_lookup_elem), 772 __imm_addr(map_hash_8b) 773 : __clobber_all); 774} 775 776SEC("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; \ 791l0_%=: r1 = *(u64*)(r0 + 0); \ 792 r1 ^= 3; \ 793 if r1 != 0 goto l1_%=; \ 794 r0 = *(u64*)(r0 + 8); \ 795l1_%=: r0 = 0; \ 796 exit; \ 797" : 798 : __imm(bpf_map_lookup_elem), 799 __imm_addr(map_hash_8b) 800 : __clobber_all); 801} 802 803SEC("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; \ 818l0_%=: r1 = *(u64*)(r0 + 0); \ 819 w1 ^= 3; \ 820 if w1 != 0 goto l1_%=; \ 821 r0 = *(u64*)(r0 + 8); \ 822l1_%=: r0 = 0; \ 823 exit; \ 824" : 825 : __imm(bpf_map_lookup_elem), 826 __imm_addr(map_hash_8b) 827 : __clobber_all); 828} 829 830SEC("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; \ 846l0_%=: r1 = *(u64*)(r0 + 0); \ 847 if r1 <= 0 goto l1_%=; \ 848 r1 ^= 3; \ 849 if r1 >= 0 goto l1_%=; \ 850 r0 = *(u64*)(r0 + 8); \ 851l1_%=: r0 = 0; \ 852 exit; \ 853" : 854 : __imm(bpf_map_lookup_elem), 855 __imm_addr(map_hash_8b) 856 : __clobber_all); 857} 858 859SEC("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; \ 875l0_%=: r1 = *(u64*)(r0 + 0); \ 876 if w1 <= 0 goto l1_%=; \ 877 w1 ^= 3; \ 878 if w1 >= 0 goto l1_%=; \ 879 r0 = *(u64*)(r0 + 8); \ 880l1_%=: r0 = 0; \ 881 exit; \ 882" : 883 : __imm(bpf_map_lookup_elem), 884 __imm_addr(map_hash_8b) 885 : __clobber_all); 886} 887 888SEC("socket") 889__description("bounds check for non const xor src dst") 890__success __log_level(2) 891__msg("5: (af) r0 ^= r6 ; R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=431,var_off=(0x0; 0x1af))") 892__naked void non_const_xor_src_dst(void) 893{ 894 asm volatile (" \ 895 call %[bpf_get_prandom_u32]; \ 896 r6 = r0; \ 897 call %[bpf_get_prandom_u32]; \ 898 r6 &= 0xaf; \ 899 r0 &= 0x1a0; \ 900 r0 ^= r6; \ 901 exit; \ 902" : 903 : __imm(bpf_map_lookup_elem), 904 __imm_addr(map_hash_8b), 905 __imm(bpf_get_prandom_u32) 906 : __clobber_all); 907} 908 909SEC("socket") 910__description("bounds check for non const or src dst") 911__success __log_level(2) 912__msg("5: (4f) r0 |= r6 ; R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=431,var_off=(0x0; 0x1af))") 913__naked void non_const_or_src_dst(void) 914{ 915 asm volatile (" \ 916 call %[bpf_get_prandom_u32]; \ 917 r6 = r0; \ 918 call %[bpf_get_prandom_u32]; \ 919 r6 &= 0xaf; \ 920 r0 &= 0x1a0; \ 921 r0 |= r6; \ 922 exit; \ 923" : 924 : __imm(bpf_map_lookup_elem), 925 __imm_addr(map_hash_8b), 926 __imm(bpf_get_prandom_u32) 927 : __clobber_all); 928} 929 930SEC("socket") 931__description("bounds check for non const mul regs") 932__success __log_level(2) 933__msg("5: (2f) r0 *= r6 ; R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=3825,var_off=(0x0; 0xfff))") 934__naked void non_const_mul_regs(void) 935{ 936 asm volatile (" \ 937 call %[bpf_get_prandom_u32]; \ 938 r6 = r0; \ 939 call %[bpf_get_prandom_u32]; \ 940 r6 &= 0xff; \ 941 r0 &= 0x0f; \ 942 r0 *= r6; \ 943 exit; \ 944" : 945 : __imm(bpf_map_lookup_elem), 946 __imm_addr(map_hash_8b), 947 __imm(bpf_get_prandom_u32) 948 : __clobber_all); 949} 950 951SEC("socket") 952__description("bounds checks after 32-bit truncation. test 1") 953__success __failure_unpriv __msg_unpriv("R0 leaks addr") 954__retval(0) 955__naked void _32_bit_truncation_test_1(void) 956{ 957 asm volatile (" \ 958 r1 = 0; \ 959 *(u64*)(r10 - 8) = r1; \ 960 r2 = r10; \ 961 r2 += -8; \ 962 r1 = %[map_hash_8b] ll; \ 963 call %[bpf_map_lookup_elem]; \ 964 if r0 == 0 goto l0_%=; \ 965 r1 = *(u32*)(r0 + 0); \ 966 /* This used to reduce the max bound to 0x7fffffff */\ 967 if r1 == 0 goto l1_%=; \ 968 if r1 > 0x7fffffff goto l0_%=; \ 969l1_%=: r0 = 0; \ 970l0_%=: exit; \ 971" : 972 : __imm(bpf_map_lookup_elem), 973 __imm_addr(map_hash_8b) 974 : __clobber_all); 975} 976 977SEC("socket") 978__description("bounds checks after 32-bit truncation. test 2") 979__success __failure_unpriv __msg_unpriv("R0 leaks addr") 980__retval(0) 981__naked void _32_bit_truncation_test_2(void) 982{ 983 asm volatile (" \ 984 r1 = 0; \ 985 *(u64*)(r10 - 8) = r1; \ 986 r2 = r10; \ 987 r2 += -8; \ 988 r1 = %[map_hash_8b] ll; \ 989 call %[bpf_map_lookup_elem]; \ 990 if r0 == 0 goto l0_%=; \ 991 r1 = *(u32*)(r0 + 0); \ 992 if r1 s< 1 goto l1_%=; \ 993 if w1 s< 0 goto l0_%=; \ 994l1_%=: r0 = 0; \ 995l0_%=: exit; \ 996" : 997 : __imm(bpf_map_lookup_elem), 998 __imm_addr(map_hash_8b) 999 : __clobber_all); 1000} 1001 1002SEC("xdp") 1003__description("bound check with JMP_JLT for crossing 64-bit signed boundary") 1004__success __retval(0) 1005__naked void crossing_64_bit_signed_boundary_1(void) 1006{ 1007 asm volatile (" \ 1008 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1009 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1010 r1 = r2; \ 1011 r1 += 1; \ 1012 if r1 > r3 goto l0_%=; \ 1013 r1 = *(u8*)(r2 + 0); \ 1014 r0 = 0x7fffffffffffff10 ll; \ 1015 r1 += r0; \ 1016 r0 = 0x8000000000000000 ll; \ 1017l1_%=: r0 += 1; \ 1018 /* r1 unsigned range is [0x7fffffffffffff10, 0x800000000000000f] */\ 1019 if r0 < r1 goto l1_%=; \ 1020l0_%=: r0 = 0; \ 1021 exit; \ 1022" : 1023 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1024 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1025 : __clobber_all); 1026} 1027 1028SEC("xdp") 1029__description("bound check with JMP_JSLT for crossing 64-bit signed boundary") 1030__success __retval(0) 1031__flag(!BPF_F_TEST_REG_INVARIANTS) /* known invariants violation */ 1032__naked void crossing_64_bit_signed_boundary_2(void) 1033{ 1034 asm volatile (" \ 1035 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1036 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1037 r1 = r2; \ 1038 r1 += 1; \ 1039 if r1 > r3 goto l0_%=; \ 1040 r1 = *(u8*)(r2 + 0); \ 1041 r0 = 0x7fffffffffffff10 ll; \ 1042 r1 += r0; \ 1043 r2 = 0x8000000000000fff ll; \ 1044 r0 = 0x8000000000000000 ll; \ 1045l1_%=: r0 += 1; \ 1046 if r0 s> r2 goto l0_%=; \ 1047 /* r1 signed range is [S64_MIN, S64_MAX] */ \ 1048 if r0 s< r1 goto l1_%=; \ 1049 r0 = 1; \ 1050 exit; \ 1051l0_%=: r0 = 0; \ 1052 exit; \ 1053" : 1054 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1055 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1056 : __clobber_all); 1057} 1058 1059SEC("xdp") 1060__description("bound check for loop upper bound greater than U32_MAX") 1061__success __retval(0) 1062__naked void bound_greater_than_u32_max(void) 1063{ 1064 asm volatile (" \ 1065 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1066 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1067 r1 = r2; \ 1068 r1 += 1; \ 1069 if r1 > r3 goto l0_%=; \ 1070 r1 = *(u8*)(r2 + 0); \ 1071 r0 = 0x100000000 ll; \ 1072 r1 += r0; \ 1073 r0 = 0x100000000 ll; \ 1074l1_%=: r0 += 1; \ 1075 if r0 < r1 goto l1_%=; \ 1076l0_%=: r0 = 0; \ 1077 exit; \ 1078" : 1079 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1080 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1081 : __clobber_all); 1082} 1083 1084SEC("xdp") 1085__description("bound check with JMP32_JLT for crossing 32-bit signed boundary") 1086__success __retval(0) 1087__naked void crossing_32_bit_signed_boundary_1(void) 1088{ 1089 asm volatile (" \ 1090 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1091 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1092 r1 = r2; \ 1093 r1 += 1; \ 1094 if r1 > r3 goto l0_%=; \ 1095 r1 = *(u8*)(r2 + 0); \ 1096 w0 = 0x7fffff10; \ 1097 w1 += w0; \ 1098 w0 = 0x80000000; \ 1099l1_%=: w0 += 1; \ 1100 /* r1 unsigned range is [0, 0x8000000f] */ \ 1101 if w0 < w1 goto l1_%=; \ 1102l0_%=: r0 = 0; \ 1103 exit; \ 1104" : 1105 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1106 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1107 : __clobber_all); 1108} 1109 1110SEC("xdp") 1111__description("bound check with JMP32_JSLT for crossing 32-bit signed boundary") 1112__success __retval(0) 1113__flag(!BPF_F_TEST_REG_INVARIANTS) /* known invariants violation */ 1114__naked void crossing_32_bit_signed_boundary_2(void) 1115{ 1116 asm volatile (" \ 1117 r2 = *(u32*)(r1 + %[xdp_md_data]); \ 1118 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \ 1119 r1 = r2; \ 1120 r1 += 1; \ 1121 if r1 > r3 goto l0_%=; \ 1122 r1 = *(u8*)(r2 + 0); \ 1123 w0 = 0x7fffff10; \ 1124 w1 += w0; \ 1125 w2 = 0x80000fff; \ 1126 w0 = 0x80000000; \ 1127l1_%=: w0 += 1; \ 1128 if w0 s> w2 goto l0_%=; \ 1129 /* r1 signed range is [S32_MIN, S32_MAX] */ \ 1130 if w0 s< w1 goto l1_%=; \ 1131 r0 = 1; \ 1132 exit; \ 1133l0_%=: r0 = 0; \ 1134 exit; \ 1135" : 1136 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)), 1137 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end)) 1138 : __clobber_all); 1139} 1140 1141SEC("tc") 1142__description("bounds check with JMP_NE for reg edge") 1143__success __retval(0) 1144__naked void reg_not_equal_const(void) 1145{ 1146 asm volatile (" \ 1147 r6 = r1; \ 1148 r1 = 0; \ 1149 *(u64*)(r10 - 8) = r1; \ 1150 call %[bpf_get_prandom_u32]; \ 1151 r4 = r0; \ 1152 r4 &= 7; \ 1153 if r4 != 0 goto l0_%=; \ 1154 r0 = 0; \ 1155 exit; \ 1156l0_%=: r1 = r6; \ 1157 r2 = 0; \ 1158 r3 = r10; \ 1159 r3 += -8; \ 1160 r5 = 0; \ 1161 /* The 4th argument of bpf_skb_store_bytes is defined as \ 1162 * ARG_CONST_SIZE, so 0 is not allowed. The 'r4 != 0' \ 1163 * is providing us this exclusion of zero from initial \ 1164 * [0, 7] range. \ 1165 */ \ 1166 call %[bpf_skb_store_bytes]; \ 1167 r0 = 0; \ 1168 exit; \ 1169" : 1170 : __imm(bpf_get_prandom_u32), 1171 __imm(bpf_skb_store_bytes) 1172 : __clobber_all); 1173} 1174 1175SEC("tc") 1176__description("bounds check with JMP_EQ for reg edge") 1177__success __retval(0) 1178__naked void reg_equal_const(void) 1179{ 1180 asm volatile (" \ 1181 r6 = r1; \ 1182 r1 = 0; \ 1183 *(u64*)(r10 - 8) = r1; \ 1184 call %[bpf_get_prandom_u32]; \ 1185 r4 = r0; \ 1186 r4 &= 7; \ 1187 if r4 == 0 goto l0_%=; \ 1188 r1 = r6; \ 1189 r2 = 0; \ 1190 r3 = r10; \ 1191 r3 += -8; \ 1192 r5 = 0; \ 1193 /* Just the same as what we do in reg_not_equal_const() */ \ 1194 call %[bpf_skb_store_bytes]; \ 1195l0_%=: r0 = 0; \ 1196 exit; \ 1197" : 1198 : __imm(bpf_get_prandom_u32), 1199 __imm(bpf_skb_store_bytes) 1200 : __clobber_all); 1201} 1202 1203char _license[] SEC("license") = "GPL"; 1204