1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include "progs/core_reloc_types.h" 4 #include <sys/mman.h> 5 #include <sys/syscall.h> 6 7 #define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name) 8 9 #define FLAVORS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 10 .a = 42, \ 11 .b = 0xc001, \ 12 .c = 0xbeef, \ 13 } 14 15 #define FLAVORS_CASE_COMMON(name) \ 16 .case_name = #name, \ 17 .bpf_obj_file = "test_core_reloc_flavors.o", \ 18 .btf_src_file = "btf__core_reloc_" #name ".o" \ 19 20 #define FLAVORS_CASE(name) { \ 21 FLAVORS_CASE_COMMON(name), \ 22 .input = FLAVORS_DATA(core_reloc_##name), \ 23 .input_len = sizeof(struct core_reloc_##name), \ 24 .output = FLAVORS_DATA(core_reloc_flavors), \ 25 .output_len = sizeof(struct core_reloc_flavors), \ 26 } 27 28 #define FLAVORS_ERR_CASE(name) { \ 29 FLAVORS_CASE_COMMON(name), \ 30 .fails = true, \ 31 } 32 33 #define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 34 .a = { .a = { .a = 42 } }, \ 35 .b = { .b = { .b = 0xc001 } }, \ 36 } 37 38 #define NESTING_CASE_COMMON(name) \ 39 .case_name = #name, \ 40 .bpf_obj_file = "test_core_reloc_nesting.o", \ 41 .btf_src_file = "btf__core_reloc_" #name ".o" 42 43 #define NESTING_CASE(name) { \ 44 NESTING_CASE_COMMON(name), \ 45 .input = NESTING_DATA(core_reloc_##name), \ 46 .input_len = sizeof(struct core_reloc_##name), \ 47 .output = NESTING_DATA(core_reloc_nesting), \ 48 .output_len = sizeof(struct core_reloc_nesting) \ 49 } 50 51 #define NESTING_ERR_CASE(name) { \ 52 NESTING_CASE_COMMON(name), \ 53 .fails = true, \ 54 } 55 56 #define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 57 .a = { [2] = 1 }, \ 58 .b = { [1] = { [2] = { [3] = 2 } } }, \ 59 .c = { [1] = { .c = 3 } }, \ 60 .d = { [0] = { [0] = { .d = 4 } } }, \ 61 } 62 63 #define ARRAYS_CASE_COMMON(name) \ 64 .case_name = #name, \ 65 .bpf_obj_file = "test_core_reloc_arrays.o", \ 66 .btf_src_file = "btf__core_reloc_" #name ".o" 67 68 #define ARRAYS_CASE(name) { \ 69 ARRAYS_CASE_COMMON(name), \ 70 .input = ARRAYS_DATA(core_reloc_##name), \ 71 .input_len = sizeof(struct core_reloc_##name), \ 72 .output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \ 73 .a2 = 1, \ 74 .b123 = 2, \ 75 .c1c = 3, \ 76 .d00d = 4, \ 77 .f10c = 0, \ 78 }, \ 79 .output_len = sizeof(struct core_reloc_arrays_output) \ 80 } 81 82 #define ARRAYS_ERR_CASE(name) { \ 83 ARRAYS_CASE_COMMON(name), \ 84 .fails = true, \ 85 } 86 87 #define PRIMITIVES_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 88 .a = 1, \ 89 .b = 2, \ 90 .c = 3, \ 91 .d = (void *)4, \ 92 .f = (void *)5, \ 93 } 94 95 #define PRIMITIVES_CASE_COMMON(name) \ 96 .case_name = #name, \ 97 .bpf_obj_file = "test_core_reloc_primitives.o", \ 98 .btf_src_file = "btf__core_reloc_" #name ".o" 99 100 #define PRIMITIVES_CASE(name) { \ 101 PRIMITIVES_CASE_COMMON(name), \ 102 .input = PRIMITIVES_DATA(core_reloc_##name), \ 103 .input_len = sizeof(struct core_reloc_##name), \ 104 .output = PRIMITIVES_DATA(core_reloc_primitives), \ 105 .output_len = sizeof(struct core_reloc_primitives), \ 106 } 107 108 #define PRIMITIVES_ERR_CASE(name) { \ 109 PRIMITIVES_CASE_COMMON(name), \ 110 .fails = true, \ 111 } 112 113 #define MODS_CASE(name) { \ 114 .case_name = #name, \ 115 .bpf_obj_file = "test_core_reloc_mods.o", \ 116 .btf_src_file = "btf__core_reloc_" #name ".o", \ 117 .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) { \ 118 .a = 1, \ 119 .b = 2, \ 120 .c = (void *)3, \ 121 .d = (void *)4, \ 122 .e = { [2] = 5 }, \ 123 .f = { [1] = 6 }, \ 124 .g = { .x = 7 }, \ 125 .h = { .y = 8 }, \ 126 }, \ 127 .input_len = sizeof(struct core_reloc_##name), \ 128 .output = STRUCT_TO_CHAR_PTR(core_reloc_mods_output) { \ 129 .a = 1, .b = 2, .c = 3, .d = 4, \ 130 .e = 5, .f = 6, .g = 7, .h = 8, \ 131 }, \ 132 .output_len = sizeof(struct core_reloc_mods_output), \ 133 } 134 135 #define PTR_AS_ARR_CASE(name) { \ 136 .case_name = #name, \ 137 .bpf_obj_file = "test_core_reloc_ptr_as_arr.o", \ 138 .btf_src_file = "btf__core_reloc_" #name ".o", \ 139 .input = (const char *)&(struct core_reloc_##name []){ \ 140 { .a = 1 }, \ 141 { .a = 2 }, \ 142 { .a = 3 }, \ 143 }, \ 144 .input_len = 3 * sizeof(struct core_reloc_##name), \ 145 .output = STRUCT_TO_CHAR_PTR(core_reloc_ptr_as_arr) { \ 146 .a = 3, \ 147 }, \ 148 .output_len = sizeof(struct core_reloc_ptr_as_arr), \ 149 } 150 151 #define INTS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \ 152 .u8_field = 1, \ 153 .s8_field = 2, \ 154 .u16_field = 3, \ 155 .s16_field = 4, \ 156 .u32_field = 5, \ 157 .s32_field = 6, \ 158 .u64_field = 7, \ 159 .s64_field = 8, \ 160 } 161 162 #define INTS_CASE_COMMON(name) \ 163 .case_name = #name, \ 164 .bpf_obj_file = "test_core_reloc_ints.o", \ 165 .btf_src_file = "btf__core_reloc_" #name ".o" 166 167 #define INTS_CASE(name) { \ 168 INTS_CASE_COMMON(name), \ 169 .input = INTS_DATA(core_reloc_##name), \ 170 .input_len = sizeof(struct core_reloc_##name), \ 171 .output = INTS_DATA(core_reloc_ints), \ 172 .output_len = sizeof(struct core_reloc_ints), \ 173 } 174 175 #define INTS_ERR_CASE(name) { \ 176 INTS_CASE_COMMON(name), \ 177 .fails = true, \ 178 } 179 180 #define EXISTENCE_CASE_COMMON(name) \ 181 .case_name = #name, \ 182 .bpf_obj_file = "test_core_reloc_existence.o", \ 183 .btf_src_file = "btf__core_reloc_" #name ".o", \ 184 .relaxed_core_relocs = true 185 186 #define EXISTENCE_ERR_CASE(name) { \ 187 EXISTENCE_CASE_COMMON(name), \ 188 .fails = true, \ 189 } 190 191 #define BITFIELDS_CASE_COMMON(objfile, test_name_prefix, name) \ 192 .case_name = test_name_prefix#name, \ 193 .bpf_obj_file = objfile, \ 194 .btf_src_file = "btf__core_reloc_" #name ".o" 195 196 #define BITFIELDS_CASE(name, ...) { \ 197 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \ 198 "direct:", name), \ 199 .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \ 200 .input_len = sizeof(struct core_reloc_##name), \ 201 .output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \ 202 __VA_ARGS__, \ 203 .output_len = sizeof(struct core_reloc_bitfields_output), \ 204 }, { \ 205 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \ 206 "probed:", name), \ 207 .input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \ 208 .input_len = sizeof(struct core_reloc_##name), \ 209 .output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \ 210 __VA_ARGS__, \ 211 .output_len = sizeof(struct core_reloc_bitfields_output), \ 212 .direct_raw_tp = true, \ 213 } 214 215 216 #define BITFIELDS_ERR_CASE(name) { \ 217 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \ 218 "probed:", name), \ 219 .fails = true, \ 220 }, { \ 221 BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \ 222 "direct:", name), \ 223 .direct_raw_tp = true, \ 224 .fails = true, \ 225 } 226 227 #define SIZE_CASE_COMMON(name) \ 228 .case_name = #name, \ 229 .bpf_obj_file = "test_core_reloc_size.o", \ 230 .btf_src_file = "btf__core_reloc_" #name ".o", \ 231 .relaxed_core_relocs = true 232 233 #define SIZE_OUTPUT_DATA(type) \ 234 STRUCT_TO_CHAR_PTR(core_reloc_size_output) { \ 235 .int_sz = sizeof(((type *)0)->int_field), \ 236 .struct_sz = sizeof(((type *)0)->struct_field), \ 237 .union_sz = sizeof(((type *)0)->union_field), \ 238 .arr_sz = sizeof(((type *)0)->arr_field), \ 239 .arr_elem_sz = sizeof(((type *)0)->arr_field[0]), \ 240 .ptr_sz = 8, /* always 8-byte pointer for BPF */ \ 241 .enum_sz = sizeof(((type *)0)->enum_field), \ 242 } 243 244 #define SIZE_CASE(name) { \ 245 SIZE_CASE_COMMON(name), \ 246 .input_len = 0, \ 247 .output = SIZE_OUTPUT_DATA(struct core_reloc_##name), \ 248 .output_len = sizeof(struct core_reloc_size_output), \ 249 } 250 251 #define SIZE_ERR_CASE(name) { \ 252 SIZE_CASE_COMMON(name), \ 253 .fails = true, \ 254 } 255 256 struct core_reloc_test_case { 257 const char *case_name; 258 const char *bpf_obj_file; 259 const char *btf_src_file; 260 const char *input; 261 int input_len; 262 const char *output; 263 int output_len; 264 bool fails; 265 bool relaxed_core_relocs; 266 bool direct_raw_tp; 267 }; 268 269 static struct core_reloc_test_case test_cases[] = { 270 /* validate we can find kernel image and use its BTF for relocs */ 271 { 272 .case_name = "kernel", 273 .bpf_obj_file = "test_core_reloc_kernel.o", 274 .btf_src_file = NULL, /* load from /lib/modules/$(uname -r) */ 275 .input = "", 276 .input_len = 0, 277 .output = STRUCT_TO_CHAR_PTR(core_reloc_kernel_output) { 278 .valid = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, 279 .comm = "test_progs", 280 .comm_len = sizeof("test_progs"), 281 }, 282 .output_len = sizeof(struct core_reloc_kernel_output), 283 }, 284 285 /* validate BPF program can use multiple flavors to match against 286 * single target BTF type 287 */ 288 FLAVORS_CASE(flavors), 289 290 FLAVORS_ERR_CASE(flavors__err_wrong_name), 291 292 /* various struct/enum nesting and resolution scenarios */ 293 NESTING_CASE(nesting), 294 NESTING_CASE(nesting___anon_embed), 295 NESTING_CASE(nesting___struct_union_mixup), 296 NESTING_CASE(nesting___extra_nesting), 297 NESTING_CASE(nesting___dup_compat_types), 298 299 NESTING_ERR_CASE(nesting___err_missing_field), 300 NESTING_ERR_CASE(nesting___err_array_field), 301 NESTING_ERR_CASE(nesting___err_missing_container), 302 NESTING_ERR_CASE(nesting___err_nonstruct_container), 303 NESTING_ERR_CASE(nesting___err_array_container), 304 NESTING_ERR_CASE(nesting___err_dup_incompat_types), 305 NESTING_ERR_CASE(nesting___err_partial_match_dups), 306 NESTING_ERR_CASE(nesting___err_too_deep), 307 308 /* various array access relocation scenarios */ 309 ARRAYS_CASE(arrays), 310 ARRAYS_CASE(arrays___diff_arr_dim), 311 ARRAYS_CASE(arrays___diff_arr_val_sz), 312 ARRAYS_CASE(arrays___equiv_zero_sz_arr), 313 ARRAYS_CASE(arrays___fixed_arr), 314 315 ARRAYS_ERR_CASE(arrays___err_too_small), 316 ARRAYS_ERR_CASE(arrays___err_too_shallow), 317 ARRAYS_ERR_CASE(arrays___err_non_array), 318 ARRAYS_ERR_CASE(arrays___err_wrong_val_type1), 319 ARRAYS_ERR_CASE(arrays___err_wrong_val_type2), 320 ARRAYS_ERR_CASE(arrays___err_bad_zero_sz_arr), 321 322 /* enum/ptr/int handling scenarios */ 323 PRIMITIVES_CASE(primitives), 324 PRIMITIVES_CASE(primitives___diff_enum_def), 325 PRIMITIVES_CASE(primitives___diff_func_proto), 326 PRIMITIVES_CASE(primitives___diff_ptr_type), 327 328 PRIMITIVES_ERR_CASE(primitives___err_non_enum), 329 PRIMITIVES_ERR_CASE(primitives___err_non_int), 330 PRIMITIVES_ERR_CASE(primitives___err_non_ptr), 331 332 /* const/volatile/restrict and typedefs scenarios */ 333 MODS_CASE(mods), 334 MODS_CASE(mods___mod_swap), 335 MODS_CASE(mods___typedefs), 336 337 /* handling "ptr is an array" semantics */ 338 PTR_AS_ARR_CASE(ptr_as_arr), 339 PTR_AS_ARR_CASE(ptr_as_arr___diff_sz), 340 341 /* int signedness/sizing/bitfield handling */ 342 INTS_CASE(ints), 343 INTS_CASE(ints___bool), 344 INTS_CASE(ints___reverse_sign), 345 346 /* validate edge cases of capturing relocations */ 347 { 348 .case_name = "misc", 349 .bpf_obj_file = "test_core_reloc_misc.o", 350 .btf_src_file = "btf__core_reloc_misc.o", 351 .input = (const char *)&(struct core_reloc_misc_extensible[]){ 352 { .a = 1 }, 353 { .a = 2 }, /* not read */ 354 { .a = 3 }, 355 }, 356 .input_len = 4 * sizeof(int), 357 .output = STRUCT_TO_CHAR_PTR(core_reloc_misc_output) { 358 .a = 1, 359 .b = 1, 360 .c = 0, /* BUG in clang, should be 3 */ 361 }, 362 .output_len = sizeof(struct core_reloc_misc_output), 363 }, 364 365 /* validate field existence checks */ 366 { 367 EXISTENCE_CASE_COMMON(existence), 368 .input = STRUCT_TO_CHAR_PTR(core_reloc_existence) { 369 .a = 1, 370 .b = 2, 371 .c = 3, 372 .arr = { 4 }, 373 .s = { .x = 5 }, 374 }, 375 .input_len = sizeof(struct core_reloc_existence), 376 .output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) { 377 .a_exists = 1, 378 .b_exists = 1, 379 .c_exists = 1, 380 .arr_exists = 1, 381 .s_exists = 1, 382 .a_value = 1, 383 .b_value = 2, 384 .c_value = 3, 385 .arr_value = 4, 386 .s_value = 5, 387 }, 388 .output_len = sizeof(struct core_reloc_existence_output), 389 }, 390 { 391 EXISTENCE_CASE_COMMON(existence___minimal), 392 .input = STRUCT_TO_CHAR_PTR(core_reloc_existence___minimal) { 393 .a = 42, 394 }, 395 .input_len = sizeof(struct core_reloc_existence___minimal), 396 .output = STRUCT_TO_CHAR_PTR(core_reloc_existence_output) { 397 .a_exists = 1, 398 .b_exists = 0, 399 .c_exists = 0, 400 .arr_exists = 0, 401 .s_exists = 0, 402 .a_value = 42, 403 .b_value = 0xff000002u, 404 .c_value = 0xff000003u, 405 .arr_value = 0xff000004u, 406 .s_value = 0xff000005u, 407 }, 408 .output_len = sizeof(struct core_reloc_existence_output), 409 }, 410 411 EXISTENCE_ERR_CASE(existence__err_int_sz), 412 EXISTENCE_ERR_CASE(existence__err_int_type), 413 EXISTENCE_ERR_CASE(existence__err_int_kind), 414 EXISTENCE_ERR_CASE(existence__err_arr_kind), 415 EXISTENCE_ERR_CASE(existence__err_arr_value_type), 416 EXISTENCE_ERR_CASE(existence__err_struct_type), 417 418 /* bitfield relocation checks */ 419 BITFIELDS_CASE(bitfields, { 420 .ub1 = 1, 421 .ub2 = 2, 422 .ub7 = 96, 423 .sb4 = -7, 424 .sb20 = -0x76543, 425 .u32 = 0x80000000, 426 .s32 = -0x76543210, 427 }), 428 BITFIELDS_CASE(bitfields___bit_sz_change, { 429 .ub1 = 6, 430 .ub2 = 0xABCDE, 431 .ub7 = 1, 432 .sb4 = -1, 433 .sb20 = -0x17654321, 434 .u32 = 0xBEEF, 435 .s32 = -0x3FEDCBA987654321LL, 436 }), 437 BITFIELDS_CASE(bitfields___bitfield_vs_int, { 438 .ub1 = 0xFEDCBA9876543210LL, 439 .ub2 = 0xA6, 440 .ub7 = -0x7EDCBA987654321LL, 441 .sb4 = -0x6123456789ABCDELL, 442 .sb20 = 0xD00DLL, 443 .u32 = -0x76543, 444 .s32 = 0x0ADEADBEEFBADB0BLL, 445 }), 446 BITFIELDS_CASE(bitfields___just_big_enough, { 447 .ub1 = 0xFLL, 448 .ub2 = 0x0812345678FEDCBALL, 449 }), 450 BITFIELDS_ERR_CASE(bitfields___err_too_big_bitfield), 451 452 /* size relocation checks */ 453 SIZE_CASE(size), 454 SIZE_CASE(size___diff_sz), 455 }; 456 457 struct data { 458 char in[256]; 459 char out[256]; 460 uint64_t my_pid_tgid; 461 }; 462 463 static size_t roundup_page(size_t sz) 464 { 465 long page_size = sysconf(_SC_PAGE_SIZE); 466 return (sz + page_size - 1) / page_size * page_size; 467 } 468 469 void test_core_reloc(void) 470 { 471 const size_t mmap_sz = roundup_page(sizeof(struct data)); 472 struct bpf_object_load_attr load_attr = {}; 473 struct core_reloc_test_case *test_case; 474 const char *tp_name, *probe_name; 475 int err, duration = 0, i, equal; 476 struct bpf_link *link = NULL; 477 struct bpf_map *data_map; 478 struct bpf_program *prog; 479 struct bpf_object *obj; 480 uint64_t my_pid_tgid; 481 struct data *data; 482 void *mmap_data = NULL; 483 484 my_pid_tgid = getpid() | ((uint64_t)syscall(SYS_gettid) << 32); 485 486 for (i = 0; i < ARRAY_SIZE(test_cases); i++) { 487 test_case = &test_cases[i]; 488 if (!test__start_subtest(test_case->case_name)) 489 continue; 490 491 DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts, 492 .relaxed_core_relocs = test_case->relaxed_core_relocs, 493 ); 494 495 obj = bpf_object__open_file(test_case->bpf_obj_file, &opts); 496 if (CHECK(IS_ERR(obj), "obj_open", "failed to open '%s': %ld\n", 497 test_case->bpf_obj_file, PTR_ERR(obj))) 498 continue; 499 500 /* for typed raw tracepoints, NULL should be specified */ 501 if (test_case->direct_raw_tp) { 502 probe_name = "tp_btf/sys_enter"; 503 tp_name = NULL; 504 } else { 505 probe_name = "raw_tracepoint/sys_enter"; 506 tp_name = "sys_enter"; 507 } 508 509 prog = bpf_object__find_program_by_title(obj, probe_name); 510 if (CHECK(!prog, "find_probe", 511 "prog '%s' not found\n", probe_name)) 512 goto cleanup; 513 514 load_attr.obj = obj; 515 load_attr.log_level = 0; 516 load_attr.target_btf_path = test_case->btf_src_file; 517 err = bpf_object__load_xattr(&load_attr); 518 if (test_case->fails) { 519 CHECK(!err, "obj_load_fail", 520 "should fail to load prog '%s'\n", probe_name); 521 goto cleanup; 522 } else { 523 if (CHECK(err, "obj_load", 524 "failed to load prog '%s': %d\n", 525 probe_name, err)) 526 goto cleanup; 527 } 528 529 data_map = bpf_object__find_map_by_name(obj, "test_cor.bss"); 530 if (CHECK(!data_map, "find_data_map", "data map not found\n")) 531 goto cleanup; 532 533 mmap_data = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE, 534 MAP_SHARED, bpf_map__fd(data_map), 0); 535 if (CHECK(mmap_data == MAP_FAILED, "mmap", 536 ".bss mmap failed: %d", errno)) { 537 mmap_data = NULL; 538 goto cleanup; 539 } 540 data = mmap_data; 541 542 memset(mmap_data, 0, sizeof(*data)); 543 memcpy(data->in, test_case->input, test_case->input_len); 544 data->my_pid_tgid = my_pid_tgid; 545 546 link = bpf_program__attach_raw_tracepoint(prog, tp_name); 547 if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", 548 PTR_ERR(link))) 549 goto cleanup; 550 551 /* trigger test run */ 552 usleep(1); 553 554 equal = memcmp(data->out, test_case->output, 555 test_case->output_len) == 0; 556 if (CHECK(!equal, "check_result", 557 "input/output data don't match\n")) { 558 int j; 559 560 for (j = 0; j < test_case->input_len; j++) { 561 printf("input byte #%d: 0x%02hhx\n", 562 j, test_case->input[j]); 563 } 564 for (j = 0; j < test_case->output_len; j++) { 565 printf("output byte #%d: EXP 0x%02hhx GOT 0x%02hhx\n", 566 j, test_case->output[j], data->out[j]); 567 } 568 goto cleanup; 569 } 570 571 cleanup: 572 if (mmap_data) { 573 CHECK_FAIL(munmap(mmap_data, mmap_sz)); 574 mmap_data = NULL; 575 } 576 if (!IS_ERR_OR_NULL(link)) { 577 bpf_link__destroy(link); 578 link = NULL; 579 } 580 bpf_object__close(obj); 581 } 582 } 583