1 // SPDX-License-Identifier: GPL-2.0 2 /* Converted from tools/testing/selftests/bpf/verifier/array_access.c */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include "bpf_misc.h" 7 8 #define MAX_ENTRIES 11 9 10 struct test_val { 11 unsigned int index; 12 int foo[MAX_ENTRIES]; 13 }; 14 15 struct { 16 __uint(type, BPF_MAP_TYPE_ARRAY); 17 __uint(max_entries, 1); 18 __type(key, int); 19 __type(value, struct test_val); 20 __uint(map_flags, BPF_F_RDONLY_PROG); 21 } map_array_ro SEC(".maps"); 22 23 struct { 24 __uint(type, BPF_MAP_TYPE_ARRAY); 25 __uint(max_entries, 1); 26 __type(key, int); 27 __type(value, struct test_val); 28 __uint(map_flags, BPF_F_WRONLY_PROG); 29 } map_array_wo SEC(".maps"); 30 31 struct { 32 __uint(type, BPF_MAP_TYPE_HASH); 33 __uint(max_entries, 1); 34 __type(key, long long); 35 __type(value, struct test_val); 36 } map_hash_48b SEC(".maps"); 37 38 SEC("socket") 39 __description("valid map access into an array with a constant") 40 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 41 __retval(0) 42 __naked void an_array_with_a_constant_1(void) 43 { 44 asm volatile (" \ 45 r1 = 0; \ 46 *(u64*)(r10 - 8) = r1; \ 47 r2 = r10; \ 48 r2 += -8; \ 49 r1 = %[map_hash_48b] ll; \ 50 call %[bpf_map_lookup_elem]; \ 51 if r0 == 0 goto l0_%=; \ 52 r1 = %[test_val_foo]; \ 53 *(u64*)(r0 + 0) = r1; \ 54 l0_%=: exit; \ 55 " : 56 : __imm(bpf_map_lookup_elem), 57 __imm_addr(map_hash_48b), 58 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 59 : __clobber_all); 60 } 61 62 SEC("socket") 63 __description("valid map access into an array with a register") 64 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 65 __retval(0) __flag(BPF_F_ANY_ALIGNMENT) 66 __naked void an_array_with_a_register_1(void) 67 { 68 asm volatile (" \ 69 r1 = 0; \ 70 *(u64*)(r10 - 8) = r1; \ 71 r2 = r10; \ 72 r2 += -8; \ 73 r1 = %[map_hash_48b] ll; \ 74 call %[bpf_map_lookup_elem]; \ 75 if r0 == 0 goto l0_%=; \ 76 r1 = 4; \ 77 r1 <<= 2; \ 78 r0 += r1; \ 79 r1 = %[test_val_foo]; \ 80 *(u64*)(r0 + 0) = r1; \ 81 l0_%=: exit; \ 82 " : 83 : __imm(bpf_map_lookup_elem), 84 __imm_addr(map_hash_48b), 85 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 86 : __clobber_all); 87 } 88 89 SEC("socket") 90 __description("valid map access into an array with a variable") 91 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 92 __retval(0) __flag(BPF_F_ANY_ALIGNMENT) 93 __naked void an_array_with_a_variable_1(void) 94 { 95 asm volatile (" \ 96 r1 = 0; \ 97 *(u64*)(r10 - 8) = r1; \ 98 r2 = r10; \ 99 r2 += -8; \ 100 r1 = %[map_hash_48b] ll; \ 101 call %[bpf_map_lookup_elem]; \ 102 if r0 == 0 goto l0_%=; \ 103 r1 = *(u32*)(r0 + 0); \ 104 if r1 >= %[max_entries] goto l0_%=; \ 105 r1 <<= 2; \ 106 r0 += r1; \ 107 r1 = %[test_val_foo]; \ 108 *(u64*)(r0 + 0) = r1; \ 109 l0_%=: exit; \ 110 " : 111 : __imm(bpf_map_lookup_elem), 112 __imm_addr(map_hash_48b), 113 __imm_const(max_entries, MAX_ENTRIES), 114 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 115 : __clobber_all); 116 } 117 118 SEC("socket") 119 __description("valid map access into an array with a signed variable") 120 __success __failure_unpriv __msg_unpriv("R0 leaks addr") 121 __retval(0) __flag(BPF_F_ANY_ALIGNMENT) 122 __naked void array_with_a_signed_variable(void) 123 { 124 asm volatile (" \ 125 r1 = 0; \ 126 *(u64*)(r10 - 8) = r1; \ 127 r2 = r10; \ 128 r2 += -8; \ 129 r1 = %[map_hash_48b] ll; \ 130 call %[bpf_map_lookup_elem]; \ 131 if r0 == 0 goto l0_%=; \ 132 r1 = *(u32*)(r0 + 0); \ 133 if w1 s> 0xffffffff goto l1_%=; \ 134 w1 = 0; \ 135 l1_%=: w2 = %[max_entries]; \ 136 if r2 s> r1 goto l2_%=; \ 137 w1 = 0; \ 138 l2_%=: w1 <<= 2; \ 139 r0 += r1; \ 140 r1 = %[test_val_foo]; \ 141 *(u64*)(r0 + 0) = r1; \ 142 l0_%=: exit; \ 143 " : 144 : __imm(bpf_map_lookup_elem), 145 __imm_addr(map_hash_48b), 146 __imm_const(max_entries, MAX_ENTRIES), 147 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 148 : __clobber_all); 149 } 150 151 SEC("socket") 152 __description("invalid map access into an array with a constant") 153 __failure __msg("invalid access to map value, value_size=48 off=48 size=8") 154 __failure_unpriv 155 __naked void an_array_with_a_constant_2(void) 156 { 157 asm volatile (" \ 158 r1 = 0; \ 159 *(u64*)(r10 - 8) = r1; \ 160 r2 = r10; \ 161 r2 += -8; \ 162 r1 = %[map_hash_48b] ll; \ 163 call %[bpf_map_lookup_elem]; \ 164 if r0 == 0 goto l0_%=; \ 165 r1 = %[test_val_foo]; \ 166 *(u64*)(r0 + %[__imm_0]) = r1; \ 167 l0_%=: exit; \ 168 " : 169 : __imm(bpf_map_lookup_elem), 170 __imm_addr(map_hash_48b), 171 __imm_const(__imm_0, (MAX_ENTRIES + 1) << 2), 172 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 173 : __clobber_all); 174 } 175 176 SEC("socket") 177 __description("invalid map access into an array with a register") 178 __failure __msg("R0 min value is outside of the allowed memory range") 179 __failure_unpriv 180 __flag(BPF_F_ANY_ALIGNMENT) 181 __naked void an_array_with_a_register_2(void) 182 { 183 asm volatile (" \ 184 r1 = 0; \ 185 *(u64*)(r10 - 8) = r1; \ 186 r2 = r10; \ 187 r2 += -8; \ 188 r1 = %[map_hash_48b] ll; \ 189 call %[bpf_map_lookup_elem]; \ 190 if r0 == 0 goto l0_%=; \ 191 r1 = %[__imm_0]; \ 192 r1 <<= 2; \ 193 r0 += r1; \ 194 r1 = %[test_val_foo]; \ 195 *(u64*)(r0 + 0) = r1; \ 196 l0_%=: exit; \ 197 " : 198 : __imm(bpf_map_lookup_elem), 199 __imm_addr(map_hash_48b), 200 __imm_const(__imm_0, MAX_ENTRIES + 1), 201 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 202 : __clobber_all); 203 } 204 205 SEC("socket") 206 __description("invalid map access into an array with a variable") 207 __failure 208 __msg("R0 unbounded memory access, make sure to bounds check any such access") 209 __failure_unpriv 210 __flag(BPF_F_ANY_ALIGNMENT) 211 __naked void an_array_with_a_variable_2(void) 212 { 213 asm volatile (" \ 214 r1 = 0; \ 215 *(u64*)(r10 - 8) = r1; \ 216 r2 = r10; \ 217 r2 += -8; \ 218 r1 = %[map_hash_48b] ll; \ 219 call %[bpf_map_lookup_elem]; \ 220 if r0 == 0 goto l0_%=; \ 221 r1 = *(u32*)(r0 + 0); \ 222 r1 <<= 2; \ 223 r0 += r1; \ 224 r1 = %[test_val_foo]; \ 225 *(u64*)(r0 + 0) = r1; \ 226 l0_%=: exit; \ 227 " : 228 : __imm(bpf_map_lookup_elem), 229 __imm_addr(map_hash_48b), 230 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 231 : __clobber_all); 232 } 233 234 SEC("socket") 235 __description("invalid map access into an array with no floor check") 236 __failure __msg("R0 unbounded memory access") 237 __failure_unpriv __msg_unpriv("R0 leaks addr") 238 __flag(BPF_F_ANY_ALIGNMENT) 239 __naked void array_with_no_floor_check(void) 240 { 241 asm volatile (" \ 242 r1 = 0; \ 243 *(u64*)(r10 - 8) = r1; \ 244 r2 = r10; \ 245 r2 += -8; \ 246 r1 = %[map_hash_48b] ll; \ 247 call %[bpf_map_lookup_elem]; \ 248 if r0 == 0 goto l0_%=; \ 249 r1 = *(u64*)(r0 + 0); \ 250 w2 = %[max_entries]; \ 251 if r2 s> r1 goto l1_%=; \ 252 w1 = 0; \ 253 l1_%=: w1 <<= 2; \ 254 r0 += r1; \ 255 r1 = %[test_val_foo]; \ 256 *(u64*)(r0 + 0) = r1; \ 257 l0_%=: exit; \ 258 " : 259 : __imm(bpf_map_lookup_elem), 260 __imm_addr(map_hash_48b), 261 __imm_const(max_entries, MAX_ENTRIES), 262 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 263 : __clobber_all); 264 } 265 266 SEC("socket") 267 __description("invalid map access into an array with a invalid max check") 268 __failure __msg("invalid access to map value, value_size=48 off=44 size=8") 269 __failure_unpriv __msg_unpriv("R0 leaks addr") 270 __flag(BPF_F_ANY_ALIGNMENT) 271 __naked void with_a_invalid_max_check_1(void) 272 { 273 asm volatile (" \ 274 r1 = 0; \ 275 *(u64*)(r10 - 8) = r1; \ 276 r2 = r10; \ 277 r2 += -8; \ 278 r1 = %[map_hash_48b] ll; \ 279 call %[bpf_map_lookup_elem]; \ 280 if r0 == 0 goto l0_%=; \ 281 r1 = *(u32*)(r0 + 0); \ 282 w2 = %[__imm_0]; \ 283 if r2 > r1 goto l1_%=; \ 284 w1 = 0; \ 285 l1_%=: w1 <<= 2; \ 286 r0 += r1; \ 287 r1 = %[test_val_foo]; \ 288 *(u64*)(r0 + 0) = r1; \ 289 l0_%=: exit; \ 290 " : 291 : __imm(bpf_map_lookup_elem), 292 __imm_addr(map_hash_48b), 293 __imm_const(__imm_0, MAX_ENTRIES + 1), 294 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 295 : __clobber_all); 296 } 297 298 SEC("socket") 299 __description("invalid map access into an array with a invalid max check") 300 __failure __msg("R0 pointer += pointer") 301 __failure_unpriv 302 __flag(BPF_F_ANY_ALIGNMENT) 303 __naked void with_a_invalid_max_check_2(void) 304 { 305 asm volatile (" \ 306 r1 = 0; \ 307 *(u64*)(r10 - 8) = r1; \ 308 r2 = r10; \ 309 r2 += -8; \ 310 r1 = %[map_hash_48b] ll; \ 311 call %[bpf_map_lookup_elem]; \ 312 if r0 == 0 goto l0_%=; \ 313 r8 = r0; \ 314 r1 = 0; \ 315 *(u64*)(r10 - 8) = r1; \ 316 r2 = r10; \ 317 r2 += -8; \ 318 r1 = %[map_hash_48b] ll; \ 319 call %[bpf_map_lookup_elem]; \ 320 if r0 == 0 goto l0_%=; \ 321 r0 += r8; \ 322 r0 = *(u32*)(r0 + %[test_val_foo]); \ 323 l0_%=: exit; \ 324 " : 325 : __imm(bpf_map_lookup_elem), 326 __imm_addr(map_hash_48b), 327 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 328 : __clobber_all); 329 } 330 331 SEC("socket") 332 __description("valid read map access into a read-only array 1") 333 __success __success_unpriv __retval(28) 334 __naked void a_read_only_array_1_1(void) 335 { 336 asm volatile (" \ 337 r1 = 0; \ 338 *(u64*)(r10 - 8) = r1; \ 339 r2 = r10; \ 340 r2 += -8; \ 341 r1 = %[map_array_ro] ll; \ 342 call %[bpf_map_lookup_elem]; \ 343 if r0 == 0 goto l0_%=; \ 344 r0 = *(u32*)(r0 + 0); \ 345 l0_%=: exit; \ 346 " : 347 : __imm(bpf_map_lookup_elem), 348 __imm_addr(map_array_ro) 349 : __clobber_all); 350 } 351 352 SEC("tc") 353 __description("valid read map access into a read-only array 2") 354 __success __retval(65507) 355 __naked void a_read_only_array_2_1(void) 356 { 357 asm volatile (" \ 358 r1 = 0; \ 359 *(u64*)(r10 - 8) = r1; \ 360 r2 = r10; \ 361 r2 += -8; \ 362 r1 = %[map_array_ro] ll; \ 363 call %[bpf_map_lookup_elem]; \ 364 if r0 == 0 goto l0_%=; \ 365 r1 = r0; \ 366 r2 = 4; \ 367 r3 = 0; \ 368 r4 = 0; \ 369 r5 = 0; \ 370 call %[bpf_csum_diff]; \ 371 l0_%=: exit; \ 372 " : 373 : __imm(bpf_csum_diff), 374 __imm(bpf_map_lookup_elem), 375 __imm_addr(map_array_ro) 376 : __clobber_all); 377 } 378 379 SEC("socket") 380 __description("invalid write map access into a read-only array 1") 381 __failure __msg("write into map forbidden") 382 __failure_unpriv 383 __naked void a_read_only_array_1_2(void) 384 { 385 asm volatile (" \ 386 r1 = 0; \ 387 *(u64*)(r10 - 8) = r1; \ 388 r2 = r10; \ 389 r2 += -8; \ 390 r1 = %[map_array_ro] ll; \ 391 call %[bpf_map_lookup_elem]; \ 392 if r0 == 0 goto l0_%=; \ 393 r1 = 42; \ 394 *(u64*)(r0 + 0) = r1; \ 395 l0_%=: exit; \ 396 " : 397 : __imm(bpf_map_lookup_elem), 398 __imm_addr(map_array_ro) 399 : __clobber_all); 400 } 401 402 SEC("tc") 403 __description("invalid write map access into a read-only array 2") 404 __failure __msg("write into map forbidden") 405 __naked void a_read_only_array_2_2(void) 406 { 407 asm volatile (" \ 408 r6 = r1; \ 409 r1 = 0; \ 410 *(u64*)(r10 - 8) = r1; \ 411 r2 = r10; \ 412 r2 += -8; \ 413 r1 = %[map_array_ro] ll; \ 414 call %[bpf_map_lookup_elem]; \ 415 if r0 == 0 goto l0_%=; \ 416 r1 = r6; \ 417 r2 = 0; \ 418 r3 = r0; \ 419 r4 = 8; \ 420 call %[bpf_skb_load_bytes]; \ 421 l0_%=: exit; \ 422 " : 423 : __imm(bpf_map_lookup_elem), 424 __imm(bpf_skb_load_bytes), 425 __imm_addr(map_array_ro) 426 : __clobber_all); 427 } 428 429 SEC("socket") 430 __description("valid write map access into a write-only array 1") 431 __success __success_unpriv __retval(1) 432 __naked void a_write_only_array_1_1(void) 433 { 434 asm volatile (" \ 435 r1 = 0; \ 436 *(u64*)(r10 - 8) = r1; \ 437 r2 = r10; \ 438 r2 += -8; \ 439 r1 = %[map_array_wo] ll; \ 440 call %[bpf_map_lookup_elem]; \ 441 if r0 == 0 goto l0_%=; \ 442 r1 = 42; \ 443 *(u64*)(r0 + 0) = r1; \ 444 l0_%=: r0 = 1; \ 445 exit; \ 446 " : 447 : __imm(bpf_map_lookup_elem), 448 __imm_addr(map_array_wo) 449 : __clobber_all); 450 } 451 452 SEC("tc") 453 __description("valid write map access into a write-only array 2") 454 __success __retval(0) 455 __naked void a_write_only_array_2_1(void) 456 { 457 asm volatile (" \ 458 r6 = r1; \ 459 r1 = 0; \ 460 *(u64*)(r10 - 8) = r1; \ 461 r2 = r10; \ 462 r2 += -8; \ 463 r1 = %[map_array_wo] ll; \ 464 call %[bpf_map_lookup_elem]; \ 465 if r0 == 0 goto l0_%=; \ 466 r1 = r6; \ 467 r2 = 0; \ 468 r3 = r0; \ 469 r4 = 8; \ 470 call %[bpf_skb_load_bytes]; \ 471 l0_%=: exit; \ 472 " : 473 : __imm(bpf_map_lookup_elem), 474 __imm(bpf_skb_load_bytes), 475 __imm_addr(map_array_wo) 476 : __clobber_all); 477 } 478 479 SEC("socket") 480 __description("invalid read map access into a write-only array 1") 481 __failure __msg("read from map forbidden") 482 __failure_unpriv 483 __naked void a_write_only_array_1_2(void) 484 { 485 asm volatile (" \ 486 r1 = 0; \ 487 *(u64*)(r10 - 8) = r1; \ 488 r2 = r10; \ 489 r2 += -8; \ 490 r1 = %[map_array_wo] ll; \ 491 call %[bpf_map_lookup_elem]; \ 492 if r0 == 0 goto l0_%=; \ 493 r0 = *(u64*)(r0 + 0); \ 494 l0_%=: exit; \ 495 " : 496 : __imm(bpf_map_lookup_elem), 497 __imm_addr(map_array_wo) 498 : __clobber_all); 499 } 500 501 SEC("tc") 502 __description("invalid read map access into a write-only array 2") 503 __failure __msg("read from map forbidden") 504 __naked void a_write_only_array_2_2(void) 505 { 506 asm volatile (" \ 507 r1 = 0; \ 508 *(u64*)(r10 - 8) = r1; \ 509 r2 = r10; \ 510 r2 += -8; \ 511 r1 = %[map_array_wo] ll; \ 512 call %[bpf_map_lookup_elem]; \ 513 if r0 == 0 goto l0_%=; \ 514 r1 = r0; \ 515 r2 = 4; \ 516 r3 = 0; \ 517 r4 = 0; \ 518 r5 = 0; \ 519 call %[bpf_csum_diff]; \ 520 l0_%=: exit; \ 521 " : 522 : __imm(bpf_csum_diff), 523 __imm(bpf_map_lookup_elem), 524 __imm_addr(map_array_wo) 525 : __clobber_all); 526 } 527 528 char _license[] SEC("license") = "GPL"; 529