1 // SPDX-License-Identifier: GPL-2.0 2 #include <unistd.h> 3 #include <test_progs.h> 4 #include <network_helpers.h> 5 #include "tailcall_poke.skel.h" 6 7 8 /* test_tailcall_1 checks basic functionality by patching multiple locations 9 * in a single program for a single tail call slot with nop->jmp, jmp->nop 10 * and jmp->jmp rewrites. Also checks for nop->nop. 11 */ 12 static void test_tailcall_1(void) 13 { 14 int err, map_fd, prog_fd, main_fd, i, j; 15 struct bpf_map *prog_array; 16 struct bpf_program *prog; 17 struct bpf_object *obj; 18 char prog_name[32]; 19 char buff[128] = {}; 20 LIBBPF_OPTS(bpf_test_run_opts, topts, 21 .data_in = buff, 22 .data_size_in = sizeof(buff), 23 .repeat = 1, 24 ); 25 26 err = bpf_prog_test_load("tailcall1.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj, 27 &prog_fd); 28 if (CHECK_FAIL(err)) 29 return; 30 31 prog = bpf_object__find_program_by_name(obj, "entry"); 32 if (CHECK_FAIL(!prog)) 33 goto out; 34 35 main_fd = bpf_program__fd(prog); 36 if (CHECK_FAIL(main_fd < 0)) 37 goto out; 38 39 prog_array = bpf_object__find_map_by_name(obj, "jmp_table"); 40 if (CHECK_FAIL(!prog_array)) 41 goto out; 42 43 map_fd = bpf_map__fd(prog_array); 44 if (CHECK_FAIL(map_fd < 0)) 45 goto out; 46 47 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 48 snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); 49 50 prog = bpf_object__find_program_by_name(obj, prog_name); 51 if (CHECK_FAIL(!prog)) 52 goto out; 53 54 prog_fd = bpf_program__fd(prog); 55 if (CHECK_FAIL(prog_fd < 0)) 56 goto out; 57 58 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 59 if (CHECK_FAIL(err)) 60 goto out; 61 } 62 63 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 64 err = bpf_prog_test_run_opts(main_fd, &topts); 65 ASSERT_OK(err, "tailcall"); 66 ASSERT_EQ(topts.retval, i, "tailcall retval"); 67 68 err = bpf_map_delete_elem(map_fd, &i); 69 if (CHECK_FAIL(err)) 70 goto out; 71 } 72 73 err = bpf_prog_test_run_opts(main_fd, &topts); 74 ASSERT_OK(err, "tailcall"); 75 ASSERT_EQ(topts.retval, 3, "tailcall retval"); 76 77 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 78 snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); 79 80 prog = bpf_object__find_program_by_name(obj, prog_name); 81 if (CHECK_FAIL(!prog)) 82 goto out; 83 84 prog_fd = bpf_program__fd(prog); 85 if (CHECK_FAIL(prog_fd < 0)) 86 goto out; 87 88 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 89 if (CHECK_FAIL(err)) 90 goto out; 91 } 92 93 err = bpf_prog_test_run_opts(main_fd, &topts); 94 ASSERT_OK(err, "tailcall"); 95 ASSERT_OK(topts.retval, "tailcall retval"); 96 97 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 98 j = bpf_map__max_entries(prog_array) - 1 - i; 99 snprintf(prog_name, sizeof(prog_name), "classifier_%d", j); 100 101 prog = bpf_object__find_program_by_name(obj, prog_name); 102 if (CHECK_FAIL(!prog)) 103 goto out; 104 105 prog_fd = bpf_program__fd(prog); 106 if (CHECK_FAIL(prog_fd < 0)) 107 goto out; 108 109 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 110 if (CHECK_FAIL(err)) 111 goto out; 112 } 113 114 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 115 j = bpf_map__max_entries(prog_array) - 1 - i; 116 117 err = bpf_prog_test_run_opts(main_fd, &topts); 118 ASSERT_OK(err, "tailcall"); 119 ASSERT_EQ(topts.retval, j, "tailcall retval"); 120 121 err = bpf_map_delete_elem(map_fd, &i); 122 if (CHECK_FAIL(err)) 123 goto out; 124 } 125 126 err = bpf_prog_test_run_opts(main_fd, &topts); 127 ASSERT_OK(err, "tailcall"); 128 ASSERT_EQ(topts.retval, 3, "tailcall retval"); 129 130 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 131 err = bpf_map_delete_elem(map_fd, &i); 132 if (CHECK_FAIL(err >= 0 || errno != ENOENT)) 133 goto out; 134 135 err = bpf_prog_test_run_opts(main_fd, &topts); 136 ASSERT_OK(err, "tailcall"); 137 ASSERT_EQ(topts.retval, 3, "tailcall retval"); 138 } 139 140 out: 141 bpf_object__close(obj); 142 } 143 144 /* test_tailcall_2 checks that patching multiple programs for a single 145 * tail call slot works. It also jumps through several programs and tests 146 * the tail call limit counter. 147 */ 148 static void test_tailcall_2(void) 149 { 150 int err, map_fd, prog_fd, main_fd, i; 151 struct bpf_map *prog_array; 152 struct bpf_program *prog; 153 struct bpf_object *obj; 154 char prog_name[32]; 155 char buff[128] = {}; 156 LIBBPF_OPTS(bpf_test_run_opts, topts, 157 .data_in = buff, 158 .data_size_in = sizeof(buff), 159 .repeat = 1, 160 ); 161 162 err = bpf_prog_test_load("tailcall2.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj, 163 &prog_fd); 164 if (CHECK_FAIL(err)) 165 return; 166 167 prog = bpf_object__find_program_by_name(obj, "entry"); 168 if (CHECK_FAIL(!prog)) 169 goto out; 170 171 main_fd = bpf_program__fd(prog); 172 if (CHECK_FAIL(main_fd < 0)) 173 goto out; 174 175 prog_array = bpf_object__find_map_by_name(obj, "jmp_table"); 176 if (CHECK_FAIL(!prog_array)) 177 goto out; 178 179 map_fd = bpf_map__fd(prog_array); 180 if (CHECK_FAIL(map_fd < 0)) 181 goto out; 182 183 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 184 snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); 185 186 prog = bpf_object__find_program_by_name(obj, prog_name); 187 if (CHECK_FAIL(!prog)) 188 goto out; 189 190 prog_fd = bpf_program__fd(prog); 191 if (CHECK_FAIL(prog_fd < 0)) 192 goto out; 193 194 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 195 if (CHECK_FAIL(err)) 196 goto out; 197 } 198 199 err = bpf_prog_test_run_opts(main_fd, &topts); 200 ASSERT_OK(err, "tailcall"); 201 ASSERT_EQ(topts.retval, 2, "tailcall retval"); 202 203 i = 2; 204 err = bpf_map_delete_elem(map_fd, &i); 205 if (CHECK_FAIL(err)) 206 goto out; 207 208 err = bpf_prog_test_run_opts(main_fd, &topts); 209 ASSERT_OK(err, "tailcall"); 210 ASSERT_EQ(topts.retval, 1, "tailcall retval"); 211 212 i = 0; 213 err = bpf_map_delete_elem(map_fd, &i); 214 if (CHECK_FAIL(err)) 215 goto out; 216 217 err = bpf_prog_test_run_opts(main_fd, &topts); 218 ASSERT_OK(err, "tailcall"); 219 ASSERT_EQ(topts.retval, 3, "tailcall retval"); 220 out: 221 bpf_object__close(obj); 222 } 223 224 static void test_tailcall_count(const char *which, bool test_fentry, 225 bool test_fexit) 226 { 227 struct bpf_object *obj = NULL, *fentry_obj = NULL, *fexit_obj = NULL; 228 struct bpf_link *fentry_link = NULL, *fexit_link = NULL; 229 int err, map_fd, prog_fd, main_fd, data_fd, i, val; 230 struct bpf_map *prog_array, *data_map; 231 struct bpf_program *prog; 232 char buff[128] = {}; 233 LIBBPF_OPTS(bpf_test_run_opts, topts, 234 .data_in = buff, 235 .data_size_in = sizeof(buff), 236 .repeat = 1, 237 ); 238 239 err = bpf_prog_test_load(which, BPF_PROG_TYPE_SCHED_CLS, &obj, 240 &prog_fd); 241 if (CHECK_FAIL(err)) 242 return; 243 244 prog = bpf_object__find_program_by_name(obj, "entry"); 245 if (CHECK_FAIL(!prog)) 246 goto out; 247 248 main_fd = bpf_program__fd(prog); 249 if (CHECK_FAIL(main_fd < 0)) 250 goto out; 251 252 prog_array = bpf_object__find_map_by_name(obj, "jmp_table"); 253 if (CHECK_FAIL(!prog_array)) 254 goto out; 255 256 map_fd = bpf_map__fd(prog_array); 257 if (CHECK_FAIL(map_fd < 0)) 258 goto out; 259 260 prog = bpf_object__find_program_by_name(obj, "classifier_0"); 261 if (CHECK_FAIL(!prog)) 262 goto out; 263 264 prog_fd = bpf_program__fd(prog); 265 if (CHECK_FAIL(prog_fd < 0)) 266 goto out; 267 268 i = 0; 269 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 270 if (CHECK_FAIL(err)) 271 goto out; 272 273 if (test_fentry) { 274 fentry_obj = bpf_object__open_file("tailcall_bpf2bpf_fentry.bpf.o", 275 NULL); 276 if (!ASSERT_OK_PTR(fentry_obj, "open fentry_obj file")) 277 goto out; 278 279 prog = bpf_object__find_program_by_name(fentry_obj, "fentry"); 280 if (!ASSERT_OK_PTR(prog, "find fentry prog")) 281 goto out; 282 283 err = bpf_program__set_attach_target(prog, prog_fd, 284 "subprog_tail"); 285 if (!ASSERT_OK(err, "set_attach_target subprog_tail")) 286 goto out; 287 288 err = bpf_object__load(fentry_obj); 289 if (!ASSERT_OK(err, "load fentry_obj")) 290 goto out; 291 292 fentry_link = bpf_program__attach_trace(prog); 293 if (!ASSERT_OK_PTR(fentry_link, "attach_trace")) 294 goto out; 295 } 296 297 if (test_fexit) { 298 fexit_obj = bpf_object__open_file("tailcall_bpf2bpf_fexit.bpf.o", 299 NULL); 300 if (!ASSERT_OK_PTR(fexit_obj, "open fexit_obj file")) 301 goto out; 302 303 prog = bpf_object__find_program_by_name(fexit_obj, "fexit"); 304 if (!ASSERT_OK_PTR(prog, "find fexit prog")) 305 goto out; 306 307 err = bpf_program__set_attach_target(prog, prog_fd, 308 "subprog_tail"); 309 if (!ASSERT_OK(err, "set_attach_target subprog_tail")) 310 goto out; 311 312 err = bpf_object__load(fexit_obj); 313 if (!ASSERT_OK(err, "load fexit_obj")) 314 goto out; 315 316 fexit_link = bpf_program__attach_trace(prog); 317 if (!ASSERT_OK_PTR(fexit_link, "attach_trace")) 318 goto out; 319 } 320 321 err = bpf_prog_test_run_opts(main_fd, &topts); 322 ASSERT_OK(err, "tailcall"); 323 ASSERT_EQ(topts.retval, 1, "tailcall retval"); 324 325 data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); 326 if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) 327 goto out; 328 329 data_fd = bpf_map__fd(data_map); 330 if (CHECK_FAIL(data_fd < 0)) 331 goto out; 332 333 i = 0; 334 err = bpf_map_lookup_elem(data_fd, &i, &val); 335 ASSERT_OK(err, "tailcall count"); 336 ASSERT_EQ(val, 33, "tailcall count"); 337 338 if (test_fentry) { 339 data_map = bpf_object__find_map_by_name(fentry_obj, ".bss"); 340 if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map), 341 "find tailcall_bpf2bpf_fentry.bss map")) 342 goto out; 343 344 data_fd = bpf_map__fd(data_map); 345 if (!ASSERT_FALSE(data_fd < 0, 346 "find tailcall_bpf2bpf_fentry.bss map fd")) 347 goto out; 348 349 i = 0; 350 err = bpf_map_lookup_elem(data_fd, &i, &val); 351 ASSERT_OK(err, "fentry count"); 352 ASSERT_EQ(val, 33, "fentry count"); 353 } 354 355 if (test_fexit) { 356 data_map = bpf_object__find_map_by_name(fexit_obj, ".bss"); 357 if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map), 358 "find tailcall_bpf2bpf_fexit.bss map")) 359 goto out; 360 361 data_fd = bpf_map__fd(data_map); 362 if (!ASSERT_FALSE(data_fd < 0, 363 "find tailcall_bpf2bpf_fexit.bss map fd")) 364 goto out; 365 366 i = 0; 367 err = bpf_map_lookup_elem(data_fd, &i, &val); 368 ASSERT_OK(err, "fexit count"); 369 ASSERT_EQ(val, 33, "fexit count"); 370 } 371 372 i = 0; 373 err = bpf_map_delete_elem(map_fd, &i); 374 if (CHECK_FAIL(err)) 375 goto out; 376 377 err = bpf_prog_test_run_opts(main_fd, &topts); 378 ASSERT_OK(err, "tailcall"); 379 ASSERT_OK(topts.retval, "tailcall retval"); 380 out: 381 bpf_link__destroy(fentry_link); 382 bpf_link__destroy(fexit_link); 383 bpf_object__close(fentry_obj); 384 bpf_object__close(fexit_obj); 385 bpf_object__close(obj); 386 } 387 388 /* test_tailcall_3 checks that the count value of the tail call limit 389 * enforcement matches with expectations. JIT uses direct jump. 390 */ 391 static void test_tailcall_3(void) 392 { 393 test_tailcall_count("tailcall3.bpf.o", false, false); 394 } 395 396 /* test_tailcall_6 checks that the count value of the tail call limit 397 * enforcement matches with expectations. JIT uses indirect jump. 398 */ 399 static void test_tailcall_6(void) 400 { 401 test_tailcall_count("tailcall6.bpf.o", false, false); 402 } 403 404 /* test_tailcall_4 checks that the kernel properly selects indirect jump 405 * for the case where the key is not known. Latter is passed via global 406 * data to select different targets we can compare return value of. 407 */ 408 static void test_tailcall_4(void) 409 { 410 int err, map_fd, prog_fd, main_fd, data_fd, i; 411 struct bpf_map *prog_array, *data_map; 412 struct bpf_program *prog; 413 struct bpf_object *obj; 414 static const int zero = 0; 415 char buff[128] = {}; 416 char prog_name[32]; 417 LIBBPF_OPTS(bpf_test_run_opts, topts, 418 .data_in = buff, 419 .data_size_in = sizeof(buff), 420 .repeat = 1, 421 ); 422 423 err = bpf_prog_test_load("tailcall4.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj, 424 &prog_fd); 425 if (CHECK_FAIL(err)) 426 return; 427 428 prog = bpf_object__find_program_by_name(obj, "entry"); 429 if (CHECK_FAIL(!prog)) 430 goto out; 431 432 main_fd = bpf_program__fd(prog); 433 if (CHECK_FAIL(main_fd < 0)) 434 goto out; 435 436 prog_array = bpf_object__find_map_by_name(obj, "jmp_table"); 437 if (CHECK_FAIL(!prog_array)) 438 goto out; 439 440 map_fd = bpf_map__fd(prog_array); 441 if (CHECK_FAIL(map_fd < 0)) 442 goto out; 443 444 data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); 445 if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) 446 goto out; 447 448 data_fd = bpf_map__fd(data_map); 449 if (CHECK_FAIL(data_fd < 0)) 450 goto out; 451 452 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 453 snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); 454 455 prog = bpf_object__find_program_by_name(obj, prog_name); 456 if (CHECK_FAIL(!prog)) 457 goto out; 458 459 prog_fd = bpf_program__fd(prog); 460 if (CHECK_FAIL(prog_fd < 0)) 461 goto out; 462 463 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 464 if (CHECK_FAIL(err)) 465 goto out; 466 } 467 468 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 469 err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY); 470 if (CHECK_FAIL(err)) 471 goto out; 472 473 err = bpf_prog_test_run_opts(main_fd, &topts); 474 ASSERT_OK(err, "tailcall"); 475 ASSERT_EQ(topts.retval, i, "tailcall retval"); 476 } 477 478 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 479 err = bpf_map_update_elem(data_fd, &zero, &i, BPF_ANY); 480 if (CHECK_FAIL(err)) 481 goto out; 482 483 err = bpf_map_delete_elem(map_fd, &i); 484 if (CHECK_FAIL(err)) 485 goto out; 486 487 err = bpf_prog_test_run_opts(main_fd, &topts); 488 ASSERT_OK(err, "tailcall"); 489 ASSERT_EQ(topts.retval, 3, "tailcall retval"); 490 } 491 out: 492 bpf_object__close(obj); 493 } 494 495 /* test_tailcall_5 probes similarly to test_tailcall_4 that the kernel generates 496 * an indirect jump when the keys are const but different from different branches. 497 */ 498 static void test_tailcall_5(void) 499 { 500 int err, map_fd, prog_fd, main_fd, data_fd, i, key[] = { 1111, 1234, 5678 }; 501 struct bpf_map *prog_array, *data_map; 502 struct bpf_program *prog; 503 struct bpf_object *obj; 504 static const int zero = 0; 505 char buff[128] = {}; 506 char prog_name[32]; 507 LIBBPF_OPTS(bpf_test_run_opts, topts, 508 .data_in = buff, 509 .data_size_in = sizeof(buff), 510 .repeat = 1, 511 ); 512 513 err = bpf_prog_test_load("tailcall5.bpf.o", BPF_PROG_TYPE_SCHED_CLS, &obj, 514 &prog_fd); 515 if (CHECK_FAIL(err)) 516 return; 517 518 prog = bpf_object__find_program_by_name(obj, "entry"); 519 if (CHECK_FAIL(!prog)) 520 goto out; 521 522 main_fd = bpf_program__fd(prog); 523 if (CHECK_FAIL(main_fd < 0)) 524 goto out; 525 526 prog_array = bpf_object__find_map_by_name(obj, "jmp_table"); 527 if (CHECK_FAIL(!prog_array)) 528 goto out; 529 530 map_fd = bpf_map__fd(prog_array); 531 if (CHECK_FAIL(map_fd < 0)) 532 goto out; 533 534 data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); 535 if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) 536 goto out; 537 538 data_fd = bpf_map__fd(data_map); 539 if (CHECK_FAIL(data_fd < 0)) 540 goto out; 541 542 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 543 snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); 544 545 prog = bpf_object__find_program_by_name(obj, prog_name); 546 if (CHECK_FAIL(!prog)) 547 goto out; 548 549 prog_fd = bpf_program__fd(prog); 550 if (CHECK_FAIL(prog_fd < 0)) 551 goto out; 552 553 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 554 if (CHECK_FAIL(err)) 555 goto out; 556 } 557 558 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 559 err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY); 560 if (CHECK_FAIL(err)) 561 goto out; 562 563 err = bpf_prog_test_run_opts(main_fd, &topts); 564 ASSERT_OK(err, "tailcall"); 565 ASSERT_EQ(topts.retval, i, "tailcall retval"); 566 } 567 568 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 569 err = bpf_map_update_elem(data_fd, &zero, &key[i], BPF_ANY); 570 if (CHECK_FAIL(err)) 571 goto out; 572 573 err = bpf_map_delete_elem(map_fd, &i); 574 if (CHECK_FAIL(err)) 575 goto out; 576 577 err = bpf_prog_test_run_opts(main_fd, &topts); 578 ASSERT_OK(err, "tailcall"); 579 ASSERT_EQ(topts.retval, 3, "tailcall retval"); 580 } 581 out: 582 bpf_object__close(obj); 583 } 584 585 /* test_tailcall_bpf2bpf_1 purpose is to make sure that tailcalls are working 586 * correctly in correlation with BPF subprograms 587 */ 588 static void test_tailcall_bpf2bpf_1(void) 589 { 590 int err, map_fd, prog_fd, main_fd, i; 591 struct bpf_map *prog_array; 592 struct bpf_program *prog; 593 struct bpf_object *obj; 594 char prog_name[32]; 595 LIBBPF_OPTS(bpf_test_run_opts, topts, 596 .data_in = &pkt_v4, 597 .data_size_in = sizeof(pkt_v4), 598 .repeat = 1, 599 ); 600 601 err = bpf_prog_test_load("tailcall_bpf2bpf1.bpf.o", BPF_PROG_TYPE_SCHED_CLS, 602 &obj, &prog_fd); 603 if (CHECK_FAIL(err)) 604 return; 605 606 prog = bpf_object__find_program_by_name(obj, "entry"); 607 if (CHECK_FAIL(!prog)) 608 goto out; 609 610 main_fd = bpf_program__fd(prog); 611 if (CHECK_FAIL(main_fd < 0)) 612 goto out; 613 614 prog_array = bpf_object__find_map_by_name(obj, "jmp_table"); 615 if (CHECK_FAIL(!prog_array)) 616 goto out; 617 618 map_fd = bpf_map__fd(prog_array); 619 if (CHECK_FAIL(map_fd < 0)) 620 goto out; 621 622 /* nop -> jmp */ 623 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 624 snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); 625 626 prog = bpf_object__find_program_by_name(obj, prog_name); 627 if (CHECK_FAIL(!prog)) 628 goto out; 629 630 prog_fd = bpf_program__fd(prog); 631 if (CHECK_FAIL(prog_fd < 0)) 632 goto out; 633 634 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 635 if (CHECK_FAIL(err)) 636 goto out; 637 } 638 639 err = bpf_prog_test_run_opts(main_fd, &topts); 640 ASSERT_OK(err, "tailcall"); 641 ASSERT_EQ(topts.retval, 1, "tailcall retval"); 642 643 /* jmp -> nop, call subprog that will do tailcall */ 644 i = 1; 645 err = bpf_map_delete_elem(map_fd, &i); 646 if (CHECK_FAIL(err)) 647 goto out; 648 649 err = bpf_prog_test_run_opts(main_fd, &topts); 650 ASSERT_OK(err, "tailcall"); 651 ASSERT_OK(topts.retval, "tailcall retval"); 652 653 /* make sure that subprog can access ctx and entry prog that 654 * called this subprog can properly return 655 */ 656 i = 0; 657 err = bpf_map_delete_elem(map_fd, &i); 658 if (CHECK_FAIL(err)) 659 goto out; 660 661 err = bpf_prog_test_run_opts(main_fd, &topts); 662 ASSERT_OK(err, "tailcall"); 663 ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 2, "tailcall retval"); 664 out: 665 bpf_object__close(obj); 666 } 667 668 /* test_tailcall_bpf2bpf_2 checks that the count value of the tail call limit 669 * enforcement matches with expectations when tailcall is preceded with 670 * bpf2bpf call. 671 */ 672 static void test_tailcall_bpf2bpf_2(void) 673 { 674 int err, map_fd, prog_fd, main_fd, data_fd, i, val; 675 struct bpf_map *prog_array, *data_map; 676 struct bpf_program *prog; 677 struct bpf_object *obj; 678 char buff[128] = {}; 679 LIBBPF_OPTS(bpf_test_run_opts, topts, 680 .data_in = buff, 681 .data_size_in = sizeof(buff), 682 .repeat = 1, 683 ); 684 685 err = bpf_prog_test_load("tailcall_bpf2bpf2.bpf.o", BPF_PROG_TYPE_SCHED_CLS, 686 &obj, &prog_fd); 687 if (CHECK_FAIL(err)) 688 return; 689 690 prog = bpf_object__find_program_by_name(obj, "entry"); 691 if (CHECK_FAIL(!prog)) 692 goto out; 693 694 main_fd = bpf_program__fd(prog); 695 if (CHECK_FAIL(main_fd < 0)) 696 goto out; 697 698 prog_array = bpf_object__find_map_by_name(obj, "jmp_table"); 699 if (CHECK_FAIL(!prog_array)) 700 goto out; 701 702 map_fd = bpf_map__fd(prog_array); 703 if (CHECK_FAIL(map_fd < 0)) 704 goto out; 705 706 prog = bpf_object__find_program_by_name(obj, "classifier_0"); 707 if (CHECK_FAIL(!prog)) 708 goto out; 709 710 prog_fd = bpf_program__fd(prog); 711 if (CHECK_FAIL(prog_fd < 0)) 712 goto out; 713 714 i = 0; 715 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 716 if (CHECK_FAIL(err)) 717 goto out; 718 719 err = bpf_prog_test_run_opts(main_fd, &topts); 720 ASSERT_OK(err, "tailcall"); 721 ASSERT_EQ(topts.retval, 1, "tailcall retval"); 722 723 data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); 724 if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) 725 goto out; 726 727 data_fd = bpf_map__fd(data_map); 728 if (CHECK_FAIL(data_fd < 0)) 729 goto out; 730 731 i = 0; 732 err = bpf_map_lookup_elem(data_fd, &i, &val); 733 ASSERT_OK(err, "tailcall count"); 734 ASSERT_EQ(val, 33, "tailcall count"); 735 736 i = 0; 737 err = bpf_map_delete_elem(map_fd, &i); 738 if (CHECK_FAIL(err)) 739 goto out; 740 741 err = bpf_prog_test_run_opts(main_fd, &topts); 742 ASSERT_OK(err, "tailcall"); 743 ASSERT_OK(topts.retval, "tailcall retval"); 744 out: 745 bpf_object__close(obj); 746 } 747 748 /* test_tailcall_bpf2bpf_3 checks that non-trivial amount of stack (up to 749 * 256 bytes) can be used within bpf subprograms that have the tailcalls 750 * in them 751 */ 752 static void test_tailcall_bpf2bpf_3(void) 753 { 754 int err, map_fd, prog_fd, main_fd, i; 755 struct bpf_map *prog_array; 756 struct bpf_program *prog; 757 struct bpf_object *obj; 758 char prog_name[32]; 759 LIBBPF_OPTS(bpf_test_run_opts, topts, 760 .data_in = &pkt_v4, 761 .data_size_in = sizeof(pkt_v4), 762 .repeat = 1, 763 ); 764 765 err = bpf_prog_test_load("tailcall_bpf2bpf3.bpf.o", BPF_PROG_TYPE_SCHED_CLS, 766 &obj, &prog_fd); 767 if (CHECK_FAIL(err)) 768 return; 769 770 prog = bpf_object__find_program_by_name(obj, "entry"); 771 if (CHECK_FAIL(!prog)) 772 goto out; 773 774 main_fd = bpf_program__fd(prog); 775 if (CHECK_FAIL(main_fd < 0)) 776 goto out; 777 778 prog_array = bpf_object__find_map_by_name(obj, "jmp_table"); 779 if (CHECK_FAIL(!prog_array)) 780 goto out; 781 782 map_fd = bpf_map__fd(prog_array); 783 if (CHECK_FAIL(map_fd < 0)) 784 goto out; 785 786 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 787 snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); 788 789 prog = bpf_object__find_program_by_name(obj, prog_name); 790 if (CHECK_FAIL(!prog)) 791 goto out; 792 793 prog_fd = bpf_program__fd(prog); 794 if (CHECK_FAIL(prog_fd < 0)) 795 goto out; 796 797 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 798 if (CHECK_FAIL(err)) 799 goto out; 800 } 801 802 err = bpf_prog_test_run_opts(main_fd, &topts); 803 ASSERT_OK(err, "tailcall"); 804 ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 3, "tailcall retval"); 805 806 i = 1; 807 err = bpf_map_delete_elem(map_fd, &i); 808 if (CHECK_FAIL(err)) 809 goto out; 810 811 err = bpf_prog_test_run_opts(main_fd, &topts); 812 ASSERT_OK(err, "tailcall"); 813 ASSERT_EQ(topts.retval, sizeof(pkt_v4), "tailcall retval"); 814 815 i = 0; 816 err = bpf_map_delete_elem(map_fd, &i); 817 if (CHECK_FAIL(err)) 818 goto out; 819 820 err = bpf_prog_test_run_opts(main_fd, &topts); 821 ASSERT_OK(err, "tailcall"); 822 ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 2, "tailcall retval"); 823 out: 824 bpf_object__close(obj); 825 } 826 827 #include "tailcall_bpf2bpf4.skel.h" 828 829 /* test_tailcall_bpf2bpf_4 checks that tailcall counter is correctly preserved 830 * across tailcalls combined with bpf2bpf calls. for making sure that tailcall 831 * counter behaves correctly, bpf program will go through following flow: 832 * 833 * entry -> entry_subprog -> tailcall0 -> bpf_func0 -> subprog0 -> 834 * -> tailcall1 -> bpf_func1 -> subprog1 -> tailcall2 -> bpf_func2 -> 835 * subprog2 [here bump global counter] --------^ 836 * 837 * We go through first two tailcalls and start counting from the subprog2 where 838 * the loop begins. At the end of the test make sure that the global counter is 839 * equal to 31, because tailcall counter includes the first two tailcalls 840 * whereas global counter is incremented only on loop presented on flow above. 841 * 842 * The noise parameter is used to insert bpf_map_update calls into the logic 843 * to force verifier to patch instructions. This allows us to ensure jump 844 * logic remains correct with instruction movement. 845 */ 846 static void test_tailcall_bpf2bpf_4(bool noise) 847 { 848 int err, map_fd, prog_fd, main_fd, data_fd, i; 849 struct tailcall_bpf2bpf4__bss val; 850 struct bpf_map *prog_array, *data_map; 851 struct bpf_program *prog; 852 struct bpf_object *obj; 853 char prog_name[32]; 854 LIBBPF_OPTS(bpf_test_run_opts, topts, 855 .data_in = &pkt_v4, 856 .data_size_in = sizeof(pkt_v4), 857 .repeat = 1, 858 ); 859 860 err = bpf_prog_test_load("tailcall_bpf2bpf4.bpf.o", BPF_PROG_TYPE_SCHED_CLS, 861 &obj, &prog_fd); 862 if (CHECK_FAIL(err)) 863 return; 864 865 prog = bpf_object__find_program_by_name(obj, "entry"); 866 if (CHECK_FAIL(!prog)) 867 goto out; 868 869 main_fd = bpf_program__fd(prog); 870 if (CHECK_FAIL(main_fd < 0)) 871 goto out; 872 873 prog_array = bpf_object__find_map_by_name(obj, "jmp_table"); 874 if (CHECK_FAIL(!prog_array)) 875 goto out; 876 877 map_fd = bpf_map__fd(prog_array); 878 if (CHECK_FAIL(map_fd < 0)) 879 goto out; 880 881 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 882 snprintf(prog_name, sizeof(prog_name), "classifier_%d", i); 883 884 prog = bpf_object__find_program_by_name(obj, prog_name); 885 if (CHECK_FAIL(!prog)) 886 goto out; 887 888 prog_fd = bpf_program__fd(prog); 889 if (CHECK_FAIL(prog_fd < 0)) 890 goto out; 891 892 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 893 if (CHECK_FAIL(err)) 894 goto out; 895 } 896 897 data_map = bpf_object__find_map_by_name(obj, "tailcall.bss"); 898 if (CHECK_FAIL(!data_map || !bpf_map__is_internal(data_map))) 899 goto out; 900 901 data_fd = bpf_map__fd(data_map); 902 if (CHECK_FAIL(data_fd < 0)) 903 goto out; 904 905 i = 0; 906 val.noise = noise; 907 val.count = 0; 908 err = bpf_map_update_elem(data_fd, &i, &val, BPF_ANY); 909 if (CHECK_FAIL(err)) 910 goto out; 911 912 err = bpf_prog_test_run_opts(main_fd, &topts); 913 ASSERT_OK(err, "tailcall"); 914 ASSERT_EQ(topts.retval, sizeof(pkt_v4) * 3, "tailcall retval"); 915 916 i = 0; 917 err = bpf_map_lookup_elem(data_fd, &i, &val); 918 ASSERT_OK(err, "tailcall count"); 919 ASSERT_EQ(val.count, 31, "tailcall count"); 920 921 out: 922 bpf_object__close(obj); 923 } 924 925 #include "tailcall_bpf2bpf6.skel.h" 926 927 /* Tail call counting works even when there is data on stack which is 928 * not aligned to 8 bytes. 929 */ 930 static void test_tailcall_bpf2bpf_6(void) 931 { 932 struct tailcall_bpf2bpf6 *obj; 933 int err, map_fd, prog_fd, main_fd, data_fd, i, val; 934 LIBBPF_OPTS(bpf_test_run_opts, topts, 935 .data_in = &pkt_v4, 936 .data_size_in = sizeof(pkt_v4), 937 .repeat = 1, 938 ); 939 940 obj = tailcall_bpf2bpf6__open_and_load(); 941 if (!ASSERT_OK_PTR(obj, "open and load")) 942 return; 943 944 main_fd = bpf_program__fd(obj->progs.entry); 945 if (!ASSERT_GE(main_fd, 0, "entry prog fd")) 946 goto out; 947 948 map_fd = bpf_map__fd(obj->maps.jmp_table); 949 if (!ASSERT_GE(map_fd, 0, "jmp_table map fd")) 950 goto out; 951 952 prog_fd = bpf_program__fd(obj->progs.classifier_0); 953 if (!ASSERT_GE(prog_fd, 0, "classifier_0 prog fd")) 954 goto out; 955 956 i = 0; 957 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 958 if (!ASSERT_OK(err, "jmp_table map update")) 959 goto out; 960 961 err = bpf_prog_test_run_opts(main_fd, &topts); 962 ASSERT_OK(err, "entry prog test run"); 963 ASSERT_EQ(topts.retval, 0, "tailcall retval"); 964 965 data_fd = bpf_map__fd(obj->maps.bss); 966 if (!ASSERT_GE(data_fd, 0, "bss map fd")) 967 goto out; 968 969 i = 0; 970 err = bpf_map_lookup_elem(data_fd, &i, &val); 971 ASSERT_OK(err, "bss map lookup"); 972 ASSERT_EQ(val, 1, "done flag is set"); 973 974 out: 975 tailcall_bpf2bpf6__destroy(obj); 976 } 977 978 /* test_tailcall_bpf2bpf_fentry checks that the count value of the tail call 979 * limit enforcement matches with expectations when tailcall is preceded with 980 * bpf2bpf call, and the bpf2bpf call is traced by fentry. 981 */ 982 static void test_tailcall_bpf2bpf_fentry(void) 983 { 984 test_tailcall_count("tailcall_bpf2bpf2.bpf.o", true, false); 985 } 986 987 /* test_tailcall_bpf2bpf_fexit checks that the count value of the tail call 988 * limit enforcement matches with expectations when tailcall is preceded with 989 * bpf2bpf call, and the bpf2bpf call is traced by fexit. 990 */ 991 static void test_tailcall_bpf2bpf_fexit(void) 992 { 993 test_tailcall_count("tailcall_bpf2bpf2.bpf.o", false, true); 994 } 995 996 /* test_tailcall_bpf2bpf_fentry_fexit checks that the count value of the tail 997 * call limit enforcement matches with expectations when tailcall is preceded 998 * with bpf2bpf call, and the bpf2bpf call is traced by both fentry and fexit. 999 */ 1000 static void test_tailcall_bpf2bpf_fentry_fexit(void) 1001 { 1002 test_tailcall_count("tailcall_bpf2bpf2.bpf.o", true, true); 1003 } 1004 1005 /* test_tailcall_bpf2bpf_fentry_entry checks that the count value of the tail 1006 * call limit enforcement matches with expectations when tailcall is preceded 1007 * with bpf2bpf call, and the bpf2bpf caller is traced by fentry. 1008 */ 1009 static void test_tailcall_bpf2bpf_fentry_entry(void) 1010 { 1011 struct bpf_object *tgt_obj = NULL, *fentry_obj = NULL; 1012 int err, map_fd, prog_fd, data_fd, i, val; 1013 struct bpf_map *prog_array, *data_map; 1014 struct bpf_link *fentry_link = NULL; 1015 struct bpf_program *prog; 1016 char buff[128] = {}; 1017 1018 LIBBPF_OPTS(bpf_test_run_opts, topts, 1019 .data_in = buff, 1020 .data_size_in = sizeof(buff), 1021 .repeat = 1, 1022 ); 1023 1024 err = bpf_prog_test_load("tailcall_bpf2bpf2.bpf.o", 1025 BPF_PROG_TYPE_SCHED_CLS, 1026 &tgt_obj, &prog_fd); 1027 if (!ASSERT_OK(err, "load tgt_obj")) 1028 return; 1029 1030 prog_array = bpf_object__find_map_by_name(tgt_obj, "jmp_table"); 1031 if (!ASSERT_OK_PTR(prog_array, "find jmp_table map")) 1032 goto out; 1033 1034 map_fd = bpf_map__fd(prog_array); 1035 if (!ASSERT_FALSE(map_fd < 0, "find jmp_table map fd")) 1036 goto out; 1037 1038 prog = bpf_object__find_program_by_name(tgt_obj, "classifier_0"); 1039 if (!ASSERT_OK_PTR(prog, "find classifier_0 prog")) 1040 goto out; 1041 1042 prog_fd = bpf_program__fd(prog); 1043 if (!ASSERT_FALSE(prog_fd < 0, "find classifier_0 prog fd")) 1044 goto out; 1045 1046 i = 0; 1047 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 1048 if (!ASSERT_OK(err, "update jmp_table")) 1049 goto out; 1050 1051 fentry_obj = bpf_object__open_file("tailcall_bpf2bpf_fentry.bpf.o", 1052 NULL); 1053 if (!ASSERT_OK_PTR(fentry_obj, "open fentry_obj file")) 1054 goto out; 1055 1056 prog = bpf_object__find_program_by_name(fentry_obj, "fentry"); 1057 if (!ASSERT_OK_PTR(prog, "find fentry prog")) 1058 goto out; 1059 1060 err = bpf_program__set_attach_target(prog, prog_fd, "classifier_0"); 1061 if (!ASSERT_OK(err, "set_attach_target classifier_0")) 1062 goto out; 1063 1064 err = bpf_object__load(fentry_obj); 1065 if (!ASSERT_OK(err, "load fentry_obj")) 1066 goto out; 1067 1068 fentry_link = bpf_program__attach_trace(prog); 1069 if (!ASSERT_OK_PTR(fentry_link, "attach_trace")) 1070 goto out; 1071 1072 err = bpf_prog_test_run_opts(prog_fd, &topts); 1073 ASSERT_OK(err, "tailcall"); 1074 ASSERT_EQ(topts.retval, 1, "tailcall retval"); 1075 1076 data_map = bpf_object__find_map_by_name(tgt_obj, "tailcall.bss"); 1077 if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map), 1078 "find tailcall.bss map")) 1079 goto out; 1080 1081 data_fd = bpf_map__fd(data_map); 1082 if (!ASSERT_FALSE(data_fd < 0, "find tailcall.bss map fd")) 1083 goto out; 1084 1085 i = 0; 1086 err = bpf_map_lookup_elem(data_fd, &i, &val); 1087 ASSERT_OK(err, "tailcall count"); 1088 ASSERT_EQ(val, 34, "tailcall count"); 1089 1090 data_map = bpf_object__find_map_by_name(fentry_obj, ".bss"); 1091 if (!ASSERT_FALSE(!data_map || !bpf_map__is_internal(data_map), 1092 "find tailcall_bpf2bpf_fentry.bss map")) 1093 goto out; 1094 1095 data_fd = bpf_map__fd(data_map); 1096 if (!ASSERT_FALSE(data_fd < 0, 1097 "find tailcall_bpf2bpf_fentry.bss map fd")) 1098 goto out; 1099 1100 i = 0; 1101 err = bpf_map_lookup_elem(data_fd, &i, &val); 1102 ASSERT_OK(err, "fentry count"); 1103 ASSERT_EQ(val, 1, "fentry count"); 1104 1105 out: 1106 bpf_link__destroy(fentry_link); 1107 bpf_object__close(fentry_obj); 1108 bpf_object__close(tgt_obj); 1109 } 1110 1111 #define JMP_TABLE "/sys/fs/bpf/jmp_table" 1112 1113 static int poke_thread_exit; 1114 1115 static void *poke_update(void *arg) 1116 { 1117 __u32 zero = 0, prog1_fd, prog2_fd, map_fd; 1118 struct tailcall_poke *call = arg; 1119 1120 map_fd = bpf_map__fd(call->maps.jmp_table); 1121 prog1_fd = bpf_program__fd(call->progs.call1); 1122 prog2_fd = bpf_program__fd(call->progs.call2); 1123 1124 while (!poke_thread_exit) { 1125 bpf_map_update_elem(map_fd, &zero, &prog1_fd, BPF_ANY); 1126 bpf_map_update_elem(map_fd, &zero, &prog2_fd, BPF_ANY); 1127 } 1128 1129 return NULL; 1130 } 1131 1132 /* 1133 * We are trying to hit prog array update during another program load 1134 * that shares the same prog array map. 1135 * 1136 * For that we share the jmp_table map between two skeleton instances 1137 * by pinning the jmp_table to same path. Then first skeleton instance 1138 * periodically updates jmp_table in 'poke update' thread while we load 1139 * the second skeleton instance in the main thread. 1140 */ 1141 static void test_tailcall_poke(void) 1142 { 1143 struct tailcall_poke *call, *test; 1144 int err, cnt = 10; 1145 pthread_t thread; 1146 1147 unlink(JMP_TABLE); 1148 1149 call = tailcall_poke__open_and_load(); 1150 if (!ASSERT_OK_PTR(call, "tailcall_poke__open")) 1151 return; 1152 1153 err = bpf_map__pin(call->maps.jmp_table, JMP_TABLE); 1154 if (!ASSERT_OK(err, "bpf_map__pin")) 1155 goto out; 1156 1157 err = pthread_create(&thread, NULL, poke_update, call); 1158 if (!ASSERT_OK(err, "new toggler")) 1159 goto out; 1160 1161 while (cnt--) { 1162 test = tailcall_poke__open(); 1163 if (!ASSERT_OK_PTR(test, "tailcall_poke__open")) 1164 break; 1165 1166 err = bpf_map__set_pin_path(test->maps.jmp_table, JMP_TABLE); 1167 if (!ASSERT_OK(err, "bpf_map__pin")) { 1168 tailcall_poke__destroy(test); 1169 break; 1170 } 1171 1172 bpf_program__set_autoload(test->progs.test, true); 1173 bpf_program__set_autoload(test->progs.call1, false); 1174 bpf_program__set_autoload(test->progs.call2, false); 1175 1176 err = tailcall_poke__load(test); 1177 tailcall_poke__destroy(test); 1178 if (!ASSERT_OK(err, "tailcall_poke__load")) 1179 break; 1180 } 1181 1182 poke_thread_exit = 1; 1183 ASSERT_OK(pthread_join(thread, NULL), "pthread_join"); 1184 1185 out: 1186 bpf_map__unpin(call->maps.jmp_table, JMP_TABLE); 1187 tailcall_poke__destroy(call); 1188 } 1189 1190 void test_tailcalls(void) 1191 { 1192 if (test__start_subtest("tailcall_1")) 1193 test_tailcall_1(); 1194 if (test__start_subtest("tailcall_2")) 1195 test_tailcall_2(); 1196 if (test__start_subtest("tailcall_3")) 1197 test_tailcall_3(); 1198 if (test__start_subtest("tailcall_4")) 1199 test_tailcall_4(); 1200 if (test__start_subtest("tailcall_5")) 1201 test_tailcall_5(); 1202 if (test__start_subtest("tailcall_6")) 1203 test_tailcall_6(); 1204 if (test__start_subtest("tailcall_bpf2bpf_1")) 1205 test_tailcall_bpf2bpf_1(); 1206 if (test__start_subtest("tailcall_bpf2bpf_2")) 1207 test_tailcall_bpf2bpf_2(); 1208 if (test__start_subtest("tailcall_bpf2bpf_3")) 1209 test_tailcall_bpf2bpf_3(); 1210 if (test__start_subtest("tailcall_bpf2bpf_4")) 1211 test_tailcall_bpf2bpf_4(false); 1212 if (test__start_subtest("tailcall_bpf2bpf_5")) 1213 test_tailcall_bpf2bpf_4(true); 1214 if (test__start_subtest("tailcall_bpf2bpf_6")) 1215 test_tailcall_bpf2bpf_6(); 1216 if (test__start_subtest("tailcall_bpf2bpf_fentry")) 1217 test_tailcall_bpf2bpf_fentry(); 1218 if (test__start_subtest("tailcall_bpf2bpf_fexit")) 1219 test_tailcall_bpf2bpf_fexit(); 1220 if (test__start_subtest("tailcall_bpf2bpf_fentry_fexit")) 1221 test_tailcall_bpf2bpf_fentry_fexit(); 1222 if (test__start_subtest("tailcall_bpf2bpf_fentry_entry")) 1223 test_tailcall_bpf2bpf_fentry_entry(); 1224 if (test__start_subtest("tailcall_poke")) 1225 test_tailcall_poke(); 1226 } 1227
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.