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_%=: r0 &= 0xffff; \ 372 exit; \ 373 " : 374 : __imm(bpf_csum_diff), 375 __imm(bpf_map_lookup_elem), 376 __imm_addr(map_array_ro) 377 : __clobber_all); 378 } 379 380 SEC("socket") 381 __description("invalid write map access into a read-only array 1") 382 __failure __msg("write into map forbidden") 383 __failure_unpriv 384 __naked void a_read_only_array_1_2(void) 385 { 386 asm volatile (" \ 387 r1 = 0; \ 388 *(u64*)(r10 - 8) = r1; \ 389 r2 = r10; \ 390 r2 += -8; \ 391 r1 = %[map_array_ro] ll; \ 392 call %[bpf_map_lookup_elem]; \ 393 if r0 == 0 goto l0_%=; \ 394 r1 = 42; \ 395 *(u64*)(r0 + 0) = r1; \ 396 l0_%=: exit; \ 397 " : 398 : __imm(bpf_map_lookup_elem), 399 __imm_addr(map_array_ro) 400 : __clobber_all); 401 } 402 403 SEC("tc") 404 __description("invalid write map access into a read-only array 2") 405 __failure __msg("write into map forbidden") 406 __naked void a_read_only_array_2_2(void) 407 { 408 asm volatile (" \ 409 r6 = r1; \ 410 r1 = 0; \ 411 *(u64*)(r10 - 8) = r1; \ 412 r2 = r10; \ 413 r2 += -8; \ 414 r1 = %[map_array_ro] ll; \ 415 call %[bpf_map_lookup_elem]; \ 416 if r0 == 0 goto l0_%=; \ 417 r1 = r6; \ 418 r2 = 0; \ 419 r3 = r0; \ 420 r4 = 8; \ 421 call %[bpf_skb_load_bytes]; \ 422 l0_%=: exit; \ 423 " : 424 : __imm(bpf_map_lookup_elem), 425 __imm(bpf_skb_load_bytes), 426 __imm_addr(map_array_ro) 427 : __clobber_all); 428 } 429 430 SEC("socket") 431 __description("valid write map access into a write-only array 1") 432 __success __success_unpriv __retval(1) 433 __naked void a_write_only_array_1_1(void) 434 { 435 asm volatile (" \ 436 r1 = 0; \ 437 *(u64*)(r10 - 8) = r1; \ 438 r2 = r10; \ 439 r2 += -8; \ 440 r1 = %[map_array_wo] ll; \ 441 call %[bpf_map_lookup_elem]; \ 442 if r0 == 0 goto l0_%=; \ 443 r1 = 42; \ 444 *(u64*)(r0 + 0) = r1; \ 445 l0_%=: r0 = 1; \ 446 exit; \ 447 " : 448 : __imm(bpf_map_lookup_elem), 449 __imm_addr(map_array_wo) 450 : __clobber_all); 451 } 452 453 SEC("tc") 454 __description("valid write map access into a write-only array 2") 455 __success __retval(0) 456 __naked void a_write_only_array_2_1(void) 457 { 458 asm volatile (" \ 459 r6 = r1; \ 460 r1 = 0; \ 461 *(u64*)(r10 - 8) = r1; \ 462 r2 = r10; \ 463 r2 += -8; \ 464 r1 = %[map_array_wo] ll; \ 465 call %[bpf_map_lookup_elem]; \ 466 if r0 == 0 goto l0_%=; \ 467 r1 = r6; \ 468 r2 = 0; \ 469 r3 = r0; \ 470 r4 = 8; \ 471 call %[bpf_skb_load_bytes]; \ 472 l0_%=: exit; \ 473 " : 474 : __imm(bpf_map_lookup_elem), 475 __imm(bpf_skb_load_bytes), 476 __imm_addr(map_array_wo) 477 : __clobber_all); 478 } 479 480 SEC("socket") 481 __description("invalid read map access into a write-only array 1") 482 __failure __msg("read from map forbidden") 483 __failure_unpriv 484 __naked void a_write_only_array_1_2(void) 485 { 486 asm volatile (" \ 487 r1 = 0; \ 488 *(u64*)(r10 - 8) = r1; \ 489 r2 = r10; \ 490 r2 += -8; \ 491 r1 = %[map_array_wo] ll; \ 492 call %[bpf_map_lookup_elem]; \ 493 if r0 == 0 goto l0_%=; \ 494 r0 = *(u64*)(r0 + 0); \ 495 l0_%=: exit; \ 496 " : 497 : __imm(bpf_map_lookup_elem), 498 __imm_addr(map_array_wo) 499 : __clobber_all); 500 } 501 502 SEC("tc") 503 __description("invalid read map access into a write-only array 2") 504 __failure __msg("read from map forbidden") 505 __naked void a_write_only_array_2_2(void) 506 { 507 asm volatile (" \ 508 r1 = 0; \ 509 *(u64*)(r10 - 8) = r1; \ 510 r2 = r10; \ 511 r2 += -8; \ 512 r1 = %[map_array_wo] ll; \ 513 call %[bpf_map_lookup_elem]; \ 514 if r0 == 0 goto l0_%=; \ 515 r1 = r0; \ 516 r2 = 4; \ 517 r3 = 0; \ 518 r4 = 0; \ 519 r5 = 0; \ 520 call %[bpf_csum_diff]; \ 521 l0_%=: exit; \ 522 " : 523 : __imm(bpf_csum_diff), 524 __imm(bpf_map_lookup_elem), 525 __imm_addr(map_array_wo) 526 : __clobber_all); 527 } 528 529 char _license[] SEC("license") = "GPL"; 530