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 10struct test_val { 11 unsigned int index; 12 int foo[MAX_ENTRIES]; 13}; 14 15struct { 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 23struct { 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 31struct { 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 38SEC("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; \ 54l0_%=: 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 62SEC("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; \ 81l0_%=: 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 89SEC("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; \ 109l0_%=: 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 118SEC("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; \ 135l1_%=: w2 = %[max_entries]; \ 136 if r2 s> r1 goto l2_%=; \ 137 w1 = 0; \ 138l2_%=: w1 <<= 2; \ 139 r0 += r1; \ 140 r1 = %[test_val_foo]; \ 141 *(u64*)(r0 + 0) = r1; \ 142l0_%=: 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 151SEC("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; \ 167l0_%=: 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 176SEC("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; \ 196l0_%=: 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 205SEC("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; \ 226l0_%=: 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 234SEC("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; \ 253l1_%=: w1 <<= 2; \ 254 r0 += r1; \ 255 r1 = %[test_val_foo]; \ 256 *(u64*)(r0 + 0) = r1; \ 257l0_%=: 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 266SEC("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; \ 285l1_%=: w1 <<= 2; \ 286 r0 += r1; \ 287 r1 = %[test_val_foo]; \ 288 *(u64*)(r0 + 0) = r1; \ 289l0_%=: 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 298SEC("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]); \ 323l0_%=: 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 331SEC("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); \ 345l0_%=: exit; \ 346" : 347 : __imm(bpf_map_lookup_elem), 348 __imm_addr(map_array_ro) 349 : __clobber_all); 350} 351 352SEC("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]; \ 371l0_%=: 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 380SEC("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; \ 396l0_%=: exit; \ 397" : 398 : __imm(bpf_map_lookup_elem), 399 __imm_addr(map_array_ro) 400 : __clobber_all); 401} 402 403SEC("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]; \ 422l0_%=: 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 430SEC("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; \ 445l0_%=: r0 = 1; \ 446 exit; \ 447" : 448 : __imm(bpf_map_lookup_elem), 449 __imm_addr(map_array_wo) 450 : __clobber_all); 451} 452 453SEC("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]; \ 472l0_%=: 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 480SEC("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); \ 495l0_%=: exit; \ 496" : 497 : __imm(bpf_map_lookup_elem), 498 __imm_addr(map_array_wo) 499 : __clobber_all); 500} 501 502SEC("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]; \ 521l0_%=: exit; \ 522" : 523 : __imm(bpf_csum_diff), 524 __imm(bpf_map_lookup_elem), 525 __imm_addr(map_array_wo) 526 : __clobber_all); 527} 528 529char _license[] SEC("license") = "GPL"; 530