1 // SPDX-License-Identifier: GPL-2.0 2 /* Converted from tools/testing/selftests/bpf/verifier/spin_lock.c */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include "bpf_misc.h" 7 8 struct val { 9 int cnt; 10 struct bpf_spin_lock l; 11 }; 12 13 struct { 14 __uint(type, BPF_MAP_TYPE_ARRAY); 15 __uint(max_entries, 1); 16 __type(key, int); 17 __type(value, struct val); 18 } map_spin_lock SEC(".maps"); 19 20 SEC("cgroup/skb") 21 __description("spin_lock: test1 success") 22 __success __failure_unpriv __msg_unpriv("") 23 __retval(0) 24 __naked void spin_lock_test1_success(void) 25 { 26 asm volatile (" \ 27 r1 = 0; \ 28 *(u32*)(r10 - 4) = r1; \ 29 r2 = r10; \ 30 r2 += -4; \ 31 r1 = %[map_spin_lock] ll; \ 32 call %[bpf_map_lookup_elem]; \ 33 if r0 != 0 goto l0_%=; \ 34 exit; \ 35 l0_%=: r6 = r0; \ 36 r1 = r0; \ 37 r1 += 4; \ 38 call %[bpf_spin_lock]; \ 39 r1 = r6; \ 40 r1 += 4; \ 41 r0 = *(u32*)(r6 + 0); \ 42 call %[bpf_spin_unlock]; \ 43 r0 = 0; \ 44 exit; \ 45 " : 46 : __imm(bpf_map_lookup_elem), 47 __imm(bpf_spin_lock), 48 __imm(bpf_spin_unlock), 49 __imm_addr(map_spin_lock) 50 : __clobber_all); 51 } 52 53 SEC("cgroup/skb") 54 __description("spin_lock: test2 direct ld/st") 55 __failure __msg("cannot be accessed directly") 56 __failure_unpriv __msg_unpriv("") 57 __naked void lock_test2_direct_ld_st(void) 58 { 59 asm volatile (" \ 60 r1 = 0; \ 61 *(u32*)(r10 - 4) = r1; \ 62 r2 = r10; \ 63 r2 += -4; \ 64 r1 = %[map_spin_lock] ll; \ 65 call %[bpf_map_lookup_elem]; \ 66 if r0 != 0 goto l0_%=; \ 67 exit; \ 68 l0_%=: r6 = r0; \ 69 r1 = r0; \ 70 r1 += 4; \ 71 call %[bpf_spin_lock]; \ 72 r1 = r6; \ 73 r1 += 4; \ 74 r0 = *(u32*)(r1 + 0); \ 75 call %[bpf_spin_unlock]; \ 76 r0 = 0; \ 77 exit; \ 78 " : 79 : __imm(bpf_map_lookup_elem), 80 __imm(bpf_spin_lock), 81 __imm(bpf_spin_unlock), 82 __imm_addr(map_spin_lock) 83 : __clobber_all); 84 } 85 86 SEC("cgroup/skb") 87 __description("spin_lock: test3 direct ld/st") 88 __failure __msg("cannot be accessed directly") 89 __failure_unpriv __msg_unpriv("") 90 __flag(BPF_F_ANY_ALIGNMENT) 91 __naked void lock_test3_direct_ld_st(void) 92 { 93 asm volatile (" \ 94 r1 = 0; \ 95 *(u32*)(r10 - 4) = r1; \ 96 r2 = r10; \ 97 r2 += -4; \ 98 r1 = %[map_spin_lock] ll; \ 99 call %[bpf_map_lookup_elem]; \ 100 if r0 != 0 goto l0_%=; \ 101 exit; \ 102 l0_%=: r6 = r0; \ 103 r1 = r0; \ 104 r1 += 4; \ 105 call %[bpf_spin_lock]; \ 106 r1 = r6; \ 107 r1 += 4; \ 108 r0 = *(u32*)(r6 + 1); \ 109 call %[bpf_spin_unlock]; \ 110 r0 = 0; \ 111 exit; \ 112 " : 113 : __imm(bpf_map_lookup_elem), 114 __imm(bpf_spin_lock), 115 __imm(bpf_spin_unlock), 116 __imm_addr(map_spin_lock) 117 : __clobber_all); 118 } 119 120 SEC("cgroup/skb") 121 __description("spin_lock: test4 direct ld/st") 122 __failure __msg("cannot be accessed directly") 123 __failure_unpriv __msg_unpriv("") 124 __flag(BPF_F_ANY_ALIGNMENT) 125 __naked void lock_test4_direct_ld_st(void) 126 { 127 asm volatile (" \ 128 r1 = 0; \ 129 *(u32*)(r10 - 4) = r1; \ 130 r2 = r10; \ 131 r2 += -4; \ 132 r1 = %[map_spin_lock] ll; \ 133 call %[bpf_map_lookup_elem]; \ 134 if r0 != 0 goto l0_%=; \ 135 exit; \ 136 l0_%=: r6 = r0; \ 137 r1 = r0; \ 138 r1 += 4; \ 139 call %[bpf_spin_lock]; \ 140 r1 = r6; \ 141 r1 += 4; \ 142 r0 = *(u16*)(r6 + 3); \ 143 call %[bpf_spin_unlock]; \ 144 r0 = 0; \ 145 exit; \ 146 " : 147 : __imm(bpf_map_lookup_elem), 148 __imm(bpf_spin_lock), 149 __imm(bpf_spin_unlock), 150 __imm_addr(map_spin_lock) 151 : __clobber_all); 152 } 153 154 SEC("cgroup/skb") 155 __description("spin_lock: test5 call within a locked region") 156 __failure __msg("calls are not allowed") 157 __failure_unpriv __msg_unpriv("") 158 __naked void call_within_a_locked_region(void) 159 { 160 asm volatile (" \ 161 r1 = 0; \ 162 *(u32*)(r10 - 4) = r1; \ 163 r2 = r10; \ 164 r2 += -4; \ 165 r1 = %[map_spin_lock] ll; \ 166 call %[bpf_map_lookup_elem]; \ 167 if r0 != 0 goto l0_%=; \ 168 exit; \ 169 l0_%=: r6 = r0; \ 170 r1 = r0; \ 171 r1 += 4; \ 172 call %[bpf_spin_lock]; \ 173 call %[bpf_get_prandom_u32]; \ 174 r1 = r6; \ 175 r1 += 4; \ 176 call %[bpf_spin_unlock]; \ 177 r0 = 0; \ 178 exit; \ 179 " : 180 : __imm(bpf_get_prandom_u32), 181 __imm(bpf_map_lookup_elem), 182 __imm(bpf_spin_lock), 183 __imm(bpf_spin_unlock), 184 __imm_addr(map_spin_lock) 185 : __clobber_all); 186 } 187 188 SEC("cgroup/skb") 189 __description("spin_lock: test6 missing unlock") 190 __failure __msg("unlock is missing") 191 __failure_unpriv __msg_unpriv("") 192 __naked void spin_lock_test6_missing_unlock(void) 193 { 194 asm volatile (" \ 195 r1 = 0; \ 196 *(u32*)(r10 - 4) = r1; \ 197 r2 = r10; \ 198 r2 += -4; \ 199 r1 = %[map_spin_lock] ll; \ 200 call %[bpf_map_lookup_elem]; \ 201 if r0 != 0 goto l0_%=; \ 202 exit; \ 203 l0_%=: r6 = r0; \ 204 r1 = r0; \ 205 r1 += 4; \ 206 call %[bpf_spin_lock]; \ 207 r1 = r6; \ 208 r1 += 4; \ 209 r0 = *(u32*)(r6 + 0); \ 210 if r0 != 0 goto l1_%=; \ 211 call %[bpf_spin_unlock]; \ 212 l1_%=: r0 = 0; \ 213 exit; \ 214 " : 215 : __imm(bpf_map_lookup_elem), 216 __imm(bpf_spin_lock), 217 __imm(bpf_spin_unlock), 218 __imm_addr(map_spin_lock) 219 : __clobber_all); 220 } 221 222 SEC("cgroup/skb") 223 __description("spin_lock: test7 unlock without lock") 224 __failure __msg("without taking a lock") 225 __failure_unpriv __msg_unpriv("") 226 __naked void lock_test7_unlock_without_lock(void) 227 { 228 asm volatile (" \ 229 r1 = 0; \ 230 *(u32*)(r10 - 4) = r1; \ 231 r2 = r10; \ 232 r2 += -4; \ 233 r1 = %[map_spin_lock] ll; \ 234 call %[bpf_map_lookup_elem]; \ 235 if r0 != 0 goto l0_%=; \ 236 exit; \ 237 l0_%=: r6 = r0; \ 238 r1 = r0; \ 239 r1 += 4; \ 240 if r1 != 0 goto l1_%=; \ 241 call %[bpf_spin_lock]; \ 242 l1_%=: r1 = r6; \ 243 r1 += 4; \ 244 r0 = *(u32*)(r6 + 0); \ 245 call %[bpf_spin_unlock]; \ 246 r0 = 0; \ 247 exit; \ 248 " : 249 : __imm(bpf_map_lookup_elem), 250 __imm(bpf_spin_lock), 251 __imm(bpf_spin_unlock), 252 __imm_addr(map_spin_lock) 253 : __clobber_all); 254 } 255 256 SEC("cgroup/skb") 257 __description("spin_lock: test8 double lock") 258 __failure __msg("calls are not allowed") 259 __failure_unpriv __msg_unpriv("") 260 __naked void spin_lock_test8_double_lock(void) 261 { 262 asm volatile (" \ 263 r1 = 0; \ 264 *(u32*)(r10 - 4) = r1; \ 265 r2 = r10; \ 266 r2 += -4; \ 267 r1 = %[map_spin_lock] ll; \ 268 call %[bpf_map_lookup_elem]; \ 269 if r0 != 0 goto l0_%=; \ 270 exit; \ 271 l0_%=: r6 = r0; \ 272 r1 = r0; \ 273 r1 += 4; \ 274 call %[bpf_spin_lock]; \ 275 r1 = r6; \ 276 r1 += 4; \ 277 call %[bpf_spin_lock]; \ 278 r1 = r6; \ 279 r1 += 4; \ 280 r0 = *(u32*)(r6 + 0); \ 281 call %[bpf_spin_unlock]; \ 282 r0 = 0; \ 283 exit; \ 284 " : 285 : __imm(bpf_map_lookup_elem), 286 __imm(bpf_spin_lock), 287 __imm(bpf_spin_unlock), 288 __imm_addr(map_spin_lock) 289 : __clobber_all); 290 } 291 292 SEC("cgroup/skb") 293 __description("spin_lock: test9 different lock") 294 __failure __msg("unlock of different lock") 295 __failure_unpriv __msg_unpriv("") 296 __naked void spin_lock_test9_different_lock(void) 297 { 298 asm volatile (" \ 299 r1 = 0; \ 300 *(u32*)(r10 - 4) = r1; \ 301 r2 = r10; \ 302 r2 += -4; \ 303 r1 = %[map_spin_lock] ll; \ 304 call %[bpf_map_lookup_elem]; \ 305 if r0 != 0 goto l0_%=; \ 306 exit; \ 307 l0_%=: r6 = r0; \ 308 r2 = r10; \ 309 r2 += -4; \ 310 r1 = %[map_spin_lock] ll; \ 311 call %[bpf_map_lookup_elem]; \ 312 if r0 != 0 goto l1_%=; \ 313 exit; \ 314 l1_%=: r7 = r0; \ 315 r1 = r6; \ 316 r1 += 4; \ 317 call %[bpf_spin_lock]; \ 318 r1 = r7; \ 319 r1 += 4; \ 320 call %[bpf_spin_unlock]; \ 321 r0 = 0; \ 322 exit; \ 323 " : 324 : __imm(bpf_map_lookup_elem), 325 __imm(bpf_spin_lock), 326 __imm(bpf_spin_unlock), 327 __imm_addr(map_spin_lock) 328 : __clobber_all); 329 } 330 331 SEC("cgroup/skb") 332 __description("spin_lock: test10 lock in subprog without unlock") 333 __failure __msg("unlock is missing") 334 __failure_unpriv __msg_unpriv("") 335 __naked void lock_in_subprog_without_unlock(void) 336 { 337 asm volatile (" \ 338 r1 = 0; \ 339 *(u32*)(r10 - 4) = r1; \ 340 r2 = r10; \ 341 r2 += -4; \ 342 r1 = %[map_spin_lock] ll; \ 343 call %[bpf_map_lookup_elem]; \ 344 if r0 != 0 goto l0_%=; \ 345 exit; \ 346 l0_%=: r6 = r0; \ 347 r1 = r0; \ 348 r1 += 4; \ 349 call lock_in_subprog_without_unlock__1; \ 350 r1 = r6; \ 351 r1 += 4; \ 352 call %[bpf_spin_unlock]; \ 353 r0 = 1; \ 354 exit; \ 355 " : 356 : __imm(bpf_map_lookup_elem), 357 __imm(bpf_spin_unlock), 358 __imm_addr(map_spin_lock) 359 : __clobber_all); 360 } 361 362 static __naked __noinline __attribute__((used)) 363 void lock_in_subprog_without_unlock__1(void) 364 { 365 asm volatile (" \ 366 call %[bpf_spin_lock]; \ 367 r0 = 0; \ 368 exit; \ 369 " : 370 : __imm(bpf_spin_lock) 371 : __clobber_all); 372 } 373 374 SEC("tc") 375 __description("spin_lock: test11 ld_abs under lock") 376 __failure __msg("inside bpf_spin_lock") 377 __naked void test11_ld_abs_under_lock(void) 378 { 379 asm volatile (" \ 380 r6 = r1; \ 381 r1 = 0; \ 382 *(u32*)(r10 - 4) = r1; \ 383 r2 = r10; \ 384 r2 += -4; \ 385 r1 = %[map_spin_lock] ll; \ 386 call %[bpf_map_lookup_elem]; \ 387 if r0 != 0 goto l0_%=; \ 388 exit; \ 389 l0_%=: r7 = r0; \ 390 r1 = r0; \ 391 r1 += 4; \ 392 call %[bpf_spin_lock]; \ 393 r0 = *(u8*)skb[0]; \ 394 r1 = r7; \ 395 r1 += 4; \ 396 call %[bpf_spin_unlock]; \ 397 r0 = 0; \ 398 exit; \ 399 " : 400 : __imm(bpf_map_lookup_elem), 401 __imm(bpf_spin_lock), 402 __imm(bpf_spin_unlock), 403 __imm_addr(map_spin_lock) 404 : __clobber_all); 405 } 406 407 SEC("tc") 408 __description("spin_lock: regsafe compare reg->id for map value") 409 __failure __msg("bpf_spin_unlock of different lock") 410 __flag(BPF_F_TEST_STATE_FREQ) 411 __naked void reg_id_for_map_value(void) 412 { 413 asm volatile (" \ 414 r6 = r1; \ 415 r6 = *(u32*)(r6 + %[__sk_buff_mark]); \ 416 r1 = %[map_spin_lock] ll; \ 417 r9 = r1; \ 418 r2 = 0; \ 419 *(u32*)(r10 - 4) = r2; \ 420 r2 = r10; \ 421 r2 += -4; \ 422 call %[bpf_map_lookup_elem]; \ 423 if r0 != 0 goto l0_%=; \ 424 exit; \ 425 l0_%=: r7 = r0; \ 426 r1 = r9; \ 427 r2 = r10; \ 428 r2 += -4; \ 429 call %[bpf_map_lookup_elem]; \ 430 if r0 != 0 goto l1_%=; \ 431 exit; \ 432 l1_%=: r8 = r0; \ 433 r1 = r7; \ 434 r1 += 4; \ 435 call %[bpf_spin_lock]; \ 436 if r6 == 0 goto l2_%=; \ 437 goto l3_%=; \ 438 l2_%=: r7 = r8; \ 439 l3_%=: r1 = r7; \ 440 r1 += 4; \ 441 call %[bpf_spin_unlock]; \ 442 r0 = 0; \ 443 exit; \ 444 " : 445 : __imm(bpf_map_lookup_elem), 446 __imm(bpf_spin_lock), 447 __imm(bpf_spin_unlock), 448 __imm_addr(map_spin_lock), 449 __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)) 450 : __clobber_all); 451 } 452 453 /* Make sure that regsafe() compares ids for spin lock records using 454 * check_ids(): 455 * 1: r9 = map_lookup_elem(...) ; r9.id == 1 456 * 2: r8 = map_lookup_elem(...) ; r8.id == 2 457 * 3: r7 = ktime_get_ns() 458 * 4: r6 = ktime_get_ns() 459 * 5: if r6 > r7 goto <9> 460 * 6: spin_lock(r8) 461 * 7: r9 = r8 462 * 8: goto <10> 463 * 9: spin_lock(r9) 464 * 10: spin_unlock(r9) ; r9.id == 1 || r9.id == 2 and lock is active, 465 * ; second visit to (10) should be considered safe 466 * ; if check_ids() is used. 467 * 11: exit(0) 468 */ 469 470 SEC("cgroup/skb") 471 __description("spin_lock: regsafe() check_ids() similar id mappings") 472 __success __msg("29: safe") 473 __failure_unpriv __msg_unpriv("") 474 __log_level(2) __retval(0) __flag(BPF_F_TEST_STATE_FREQ) 475 __naked void check_ids_similar_id_mappings(void) 476 { 477 asm volatile (" \ 478 r1 = 0; \ 479 *(u32*)(r10 - 4) = r1; \ 480 /* r9 = map_lookup_elem(...) */ \ 481 r2 = r10; \ 482 r2 += -4; \ 483 r1 = %[map_spin_lock] ll; \ 484 call %[bpf_map_lookup_elem]; \ 485 if r0 == 0 goto l0_%=; \ 486 r9 = r0; \ 487 /* r8 = map_lookup_elem(...) */ \ 488 r2 = r10; \ 489 r2 += -4; \ 490 r1 = %[map_spin_lock] ll; \ 491 call %[bpf_map_lookup_elem]; \ 492 if r0 == 0 goto l1_%=; \ 493 r8 = r0; \ 494 /* r7 = ktime_get_ns() */ \ 495 call %[bpf_ktime_get_ns]; \ 496 r7 = r0; \ 497 /* r6 = ktime_get_ns() */ \ 498 call %[bpf_ktime_get_ns]; \ 499 r6 = r0; \ 500 /* if r6 > r7 goto +5 ; no new information about the state is derived from\ 501 * ; this check, thus produced verifier states differ\ 502 * ; only in 'insn_idx' \ 503 * spin_lock(r8) \ 504 * r9 = r8 \ 505 * goto unlock \ 506 */ \ 507 if r6 > r7 goto l2_%=; \ 508 r1 = r8; \ 509 r1 += 4; \ 510 call %[bpf_spin_lock]; \ 511 r9 = r8; \ 512 goto l3_%=; \ 513 l2_%=: /* spin_lock(r9) */ \ 514 r1 = r9; \ 515 r1 += 4; \ 516 call %[bpf_spin_lock]; \ 517 l3_%=: /* spin_unlock(r9) */ \ 518 r1 = r9; \ 519 r1 += 4; \ 520 call %[bpf_spin_unlock]; \ 521 l0_%=: /* exit(0) */ \ 522 r0 = 0; \ 523 l1_%=: exit; \ 524 " : 525 : __imm(bpf_ktime_get_ns), 526 __imm(bpf_map_lookup_elem), 527 __imm(bpf_spin_lock), 528 __imm(bpf_spin_unlock), 529 __imm_addr(map_spin_lock) 530 : __clobber_all); 531 } 532 533 char _license[] SEC("license") = "GPL"; 534