Add tests for kprobe attachment with duplicate symbols to validate the libbpf fallback using absolute kernel addresses from kallsyms. - Add bpf_testmod_dup_sym.ko test module creating a duplicate syscall wrapper symbol for vmlinux symbol preference testing - Register module in test and kmod Makefiles - Add test_attach_probe_dup_sym() with subtests covering all kprobe attachment modes (default, legacy, perf, link) - Validate kprobe attaches to vmlinux symbol by checking kprobe_res and kretprobe_res counters Signed-off-by: Andrey Grodzovsky --- tools/testing/selftests/bpf/Makefile | 2 +- .../selftests/bpf/prog_tests/attach_probe.c | 63 +++++++++++++++++++ .../testing/selftests/bpf/test_kmods/Makefile | 2 +- .../bpf/test_kmods/bpf_testmod_dup_sym.c | 45 +++++++++++++ 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 tools/testing/selftests/bpf/test_kmods/bpf_testmod_dup_sym.c diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index c6bf4dfb1495..3d20a4d1d404 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -117,7 +117,7 @@ TEST_PROGS_EXTENDED := \ test_bpftool.py TEST_KMODS := bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \ - bpf_test_modorder_y.ko bpf_test_rqspinlock.ko + bpf_test_modorder_y.ko bpf_test_rqspinlock.ko bpf_testmod_dup_sym.ko TEST_KMOD_TARGETS = $(addprefix $(OUTPUT)/,$(TEST_KMODS)) # Compile but not part of 'make run_tests' diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c index 9e77e5da7097..75c22af2f1b3 100644 --- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c +++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c @@ -4,6 +4,7 @@ #include "test_attach_probe_manual.skel.h" #include "test_attach_probe.skel.h" #include "kprobe_write_ctx.skel.h" +#include "testing_helpers.h" /* this is how USDT semaphore is actually defined, except volatile modifier */ volatile unsigned short uprobe_ref_ctr __attribute__((unused)) __attribute((section(".probes"))); @@ -123,6 +124,59 @@ static void test_attach_probe_manual(enum probe_attach_mode attach_mode) test_attach_probe_manual__destroy(skel); } +/* Test kprobe attachment with duplicate symbols. + * This test loads bpf_testmod_dup_sym.ko which creates a duplicate + * __x64_sys_nanosleep symbol. The libbpf fix should prefer the vmlinux + * symbol over the module symbol when attaching kprobes. + */ +static void test_attach_probe_dup_sym(enum probe_attach_mode attach_mode) +{ + DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, kprobe_opts); + struct bpf_link *kprobe_link, *kretprobe_link; + struct test_attach_probe_manual *skel; + int err; + + /* Load module with duplicate symbol */ + err = load_module("bpf_testmod_dup_sym.ko", false); + if (!ASSERT_OK(err, "load_bpf_testmod_dup_sym")) { + test__skip(); + return; + } + + skel = test_attach_probe_manual__open_and_load(); + if (!ASSERT_OK_PTR(skel, "skel_dup_sym_open_and_load")) + goto unload_module; + + /* manual-attach kprobe/kretprobe with duplicate symbol present */ + kprobe_opts.attach_mode = attach_mode; + kprobe_opts.retprobe = false; + kprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kprobe, + SYS_NANOSLEEP_KPROBE_NAME, + &kprobe_opts); + if (!ASSERT_OK_PTR(kprobe_link, "attach_kprobe_dup_sym")) + goto cleanup; + skel->links.handle_kprobe = kprobe_link; + + kprobe_opts.retprobe = true; + kretprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kretprobe, + SYS_NANOSLEEP_KPROBE_NAME, + &kprobe_opts); + if (!ASSERT_OK_PTR(kretprobe_link, "attach_kretprobe_dup_sym")) + goto cleanup; + skel->links.handle_kretprobe = kretprobe_link; + + /* trigger & validate kprobe && kretprobe */ + usleep(1); + + ASSERT_EQ(skel->bss->kprobe_res, 1, "check_kprobe_dup_sym_res"); + ASSERT_EQ(skel->bss->kretprobe_res, 2, "check_kretprobe_dup_sym_res"); + +cleanup: + test_attach_probe_manual__destroy(skel); +unload_module: + unload_module("bpf_testmod_dup_sym", false); +} + /* attach uprobe/uretprobe long event name testings */ static void test_attach_uprobe_long_event_name(void) { @@ -417,6 +471,15 @@ void test_attach_probe(void) if (test__start_subtest("manual-link")) test_attach_probe_manual(PROBE_ATTACH_MODE_LINK); + if (test__start_subtest("dup-sym-default")) + test_attach_probe_dup_sym(PROBE_ATTACH_MODE_DEFAULT); + if (test__start_subtest("dup-sym-legacy")) + test_attach_probe_dup_sym(PROBE_ATTACH_MODE_LEGACY); + if (test__start_subtest("dup-sym-perf")) + test_attach_probe_dup_sym(PROBE_ATTACH_MODE_PERF); + if (test__start_subtest("dup-sym-link")) + test_attach_probe_dup_sym(PROBE_ATTACH_MODE_LINK); + if (test__start_subtest("auto")) test_attach_probe_auto(skel); if (test__start_subtest("kprobe-sleepable")) diff --git a/tools/testing/selftests/bpf/test_kmods/Makefile b/tools/testing/selftests/bpf/test_kmods/Makefile index 63c4d3f6a12f..938c462a103b 100644 --- a/tools/testing/selftests/bpf/test_kmods/Makefile +++ b/tools/testing/selftests/bpf/test_kmods/Makefile @@ -8,7 +8,7 @@ Q = @ endif MODULES = bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \ - bpf_test_modorder_y.ko bpf_test_rqspinlock.ko + bpf_test_modorder_y.ko bpf_test_rqspinlock.ko bpf_testmod_dup_sym.ko $(foreach m,$(MODULES),$(eval obj-m += $(m:.ko=.o))) diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_dup_sym.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_dup_sym.c new file mode 100644 index 000000000000..98b3e085ae90 --- /dev/null +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_dup_sym.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 CrowdStrike */ +/* Test module for duplicate kprobe symbol handling */ +#include +#include +#include + +/* Duplicate symbol to test kprobe attachment with duplicate symbols. + * This creates a duplicate of the syscall wrapper used in attach_probe tests. + * The libbpf fix should handle this by preferring the vmlinux symbol. + * This function should NEVER be called - kprobes should attach to vmlinux version. + */ +#ifdef __x86_64__ +noinline int __x64_sys_nanosleep(void) +#elif defined(__s390x__) +noinline int __s390x_sys_nanosleep(void) +#elif defined(__aarch64__) +noinline int __arm64_sys_nanosleep(void) +#elif defined(__riscv) +noinline int __riscv_sys_nanosleep(void) +#else +noinline int sys_nanosleep(void) +#endif +{ + WARN_ONCE(1, "bpf_testmod_dup_sym: dummy nanosleep symbol called - this should never execute!\n"); + return -EINVAL; +} + +static int __init bpf_testmod_dup_sym_init(void) +{ + pr_info("bpf_testmod_dup_sym: loaded (duplicate symbol test module)\n"); + return 0; +} + +static void __exit bpf_testmod_dup_sym_exit(void) +{ + pr_info("bpf_testmod_dup_sym: unloaded\n"); +} + +module_init(bpf_testmod_dup_sym_init); +module_exit(bpf_testmod_dup_sym_exit); + +MODULE_AUTHOR("Andrey Grodzovsky"); +MODULE_DESCRIPTION("BPF selftest duplicate symbol module"); +MODULE_LICENSE("GPL"); -- 2.34.1