Adding fill_link_info test for uprobe_multi link. Setting up uprobes with bogus ref_ctr_offsets and cookie values to test all the bpf_link_info::uprobe_multi fields. Signed-off-by: Jiri Olsa <jolsa@xxxxxxxxxx> --- .../selftests/bpf/prog_tests/fill_link_info.c | 189 ++++++++++++++++++ .../selftests/bpf/progs/test_fill_link_info.c | 6 + 2 files changed, 195 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c index 0379872c445a..96029fcbd47f 100644 --- a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c +++ b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c @@ -7,6 +7,7 @@ #include <test_progs.h> #include "trace_helpers.h" #include "test_fill_link_info.skel.h" +#include "bpf/libbpf_internal.h" #define TP_CAT "sched" #define TP_NAME "sched_switch" @@ -303,6 +304,187 @@ static void test_kprobe_multi_fill_link_info(struct test_fill_link_info *skel, LINK_DESTROY(skel->links.kmulti_run); } +/* Initialize semaphore variables so they don't end up in bss + * section and we could get retrieve their offsets. + */ +static short uprobe_link_info_sema_1 = 1; +static short uprobe_link_info_sema_2 = 1; +static short uprobe_link_info_sema_3 = 1; + +noinline void uprobe_link_info_func_1(void) +{ + uprobe_link_info_sema_1++; + asm volatile (""); +} + +noinline void uprobe_link_info_func_2(void) +{ + uprobe_link_info_sema_2++; + asm volatile (""); +} + +noinline void uprobe_link_info_func_3(void) +{ + uprobe_link_info_sema_3++; + asm volatile (""); +} + +static int +verify_umulti_link_info(int fd, bool retprobe, int pid, __u64 *offsets, + __u64 *cookies, __u64 *ref_ctr_offsets) +{ + char path[PATH_MAX], path_buf[PATH_MAX]; + struct bpf_link_info info; + __u32 len = sizeof(info); + __u64 ref_ctr_offsets_buf[3]; + __u64 offsets_buf[3]; + __u64 cookies_buf[3]; + int i, err; + + memset(path, 0, sizeof(path)); + err = readlink("/proc/self/exe", path, sizeof(path)); + if (!ASSERT_NEQ(err, -1, "readlink")) + return -1; + + memset(&info, 0, sizeof(info)); + info.uprobe_multi.path = ptr_to_u64(path_buf); + info.uprobe_multi.path_max = sizeof(path_buf); + +again: + err = bpf_link_get_info_by_fd(fd, &info, &len); + if (!ASSERT_OK(err, "bpf_link_get_info_by_fd")) + return -1; + + if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_UPROBE_MULTI, "info.type")) + return -1; + + ASSERT_EQ(info.uprobe_multi.pid, pid ?: getpid(), "info.uprobe_multi.pid"); + ASSERT_EQ(info.uprobe_multi.count, 3, "info.uprobe_multi.count"); + ASSERT_EQ(info.uprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN, + retprobe, "info.uprobe_multi.flags.retprobe"); + ASSERT_STREQ(path_buf, path, "info.uprobe_multi.path"); + + if (!info.uprobe_multi.offsets) { + info.uprobe_multi.offsets = ptr_to_u64(offsets_buf); + info.uprobe_multi.cookies = ptr_to_u64(cookies_buf); + info.uprobe_multi.ref_ctr_offsets = ptr_to_u64(ref_ctr_offsets_buf); + goto again; + } + + for (i = 0; i < info.uprobe_multi.count; i++) { + ASSERT_EQ(offsets_buf[i], offsets[i], "info.uprobe_multi.offsets"); + ASSERT_EQ(cookies_buf[i], cookies[i], "info.uprobe_multi.cookies"); + ASSERT_EQ(ref_ctr_offsets_buf[i], ref_ctr_offsets[i], "info.uprobe_multi.ref_ctr_offsets"); + } + return 0; +} + +static void verify_umulti_invalid_user_buffer(int fd) +{ + struct bpf_link_info info; + __u32 len = sizeof(info); + char path[PATH_MAX + 1]; + __u64 offsets[3]; + int err; + + // upath_max defined, not path + memset(&info, 0, sizeof(info)); + info.uprobe_multi.path_max = 3; + err = bpf_link_get_info_by_fd(fd, &info, &len); + ASSERT_EQ(err, -EINVAL, "failed_upath_max"); + + // path has wrong pointer + memset(&info, 0, sizeof(info)); + info.uprobe_multi.path_max = PATH_MAX; + info.uprobe_multi.path = 123; + err = bpf_link_get_info_by_fd(fd, &info, &len); + ASSERT_EQ(err, -EFAULT, "failed_bad_path_ptr"); + + // count defined, not offsets + memset(&info, 0, sizeof(info)); + info.uprobe_multi.count = 3; + err = bpf_link_get_info_by_fd(fd, &info, &len); + ASSERT_EQ(err, -EINVAL, "failed_count"); + + // path_max too big + memset(&info, 0, sizeof(info)); + info.uprobe_multi.path = ptr_to_u64(path); + info.uprobe_multi.path_max = sizeof(path); + err = bpf_link_get_info_by_fd(fd, &info, &len); + ASSERT_EQ(err, -E2BIG, "failed_path_max"); + + // offsets not big enough + memset(&info, 0, sizeof(info)); + info.uprobe_multi.offsets = ptr_to_u64(offsets); + info.uprobe_multi.count = 2; + err = bpf_link_get_info_by_fd(fd, &info, &len); + ASSERT_EQ(err, -ENOSPC, "failed_small_count"); + + // offsets has wrong pointer + memset(&info, 0, sizeof(info)); + info.uprobe_multi.offsets = 123; + info.uprobe_multi.count = 3; + err = bpf_link_get_info_by_fd(fd, &info, &len); + ASSERT_EQ(err, -EFAULT, "failed_wrong_offsets"); +} + +static void test_uprobe_multi_fill_link_info(struct test_fill_link_info *skel, + bool retprobe, bool invalid, int pid) +{ + LIBBPF_OPTS(bpf_uprobe_multi_opts, opts, + .retprobe = retprobe, + ); + const char *syms[3] = { + "uprobe_link_info_func_1", + "uprobe_link_info_func_2", + "uprobe_link_info_func_3", + }; + const char *sema[3] = { + "uprobe_link_info_sema_1", + "uprobe_link_info_sema_2", + "uprobe_link_info_sema_3", + }; + __u64 cookies[3] = { + 0xdead, + 0xbeef, + 0xcafe, + }; + __u64 *offsets, *ref_ctr_offsets; + int link_fd, err; + + err = elf_resolve_syms_offsets("/proc/self/exe", 3, sema, + (unsigned long **) &ref_ctr_offsets, STT_OBJECT); + if (!ASSERT_OK(err, "elf_resolve_syms_offsets_object")) + return; + + err = elf_resolve_syms_offsets("/proc/self/exe", 3, syms, + (unsigned long **) &offsets, STT_FUNC); + if (!ASSERT_OK(err, "elf_resolve_syms_offsets_func")) + return; + + opts.syms = syms; + opts.cookies = &cookies[0]; + opts.ref_ctr_offsets = (unsigned long *) &ref_ctr_offsets[0]; + opts.cnt = ARRAY_SIZE(syms); + + skel->links.umulti_run = bpf_program__attach_uprobe_multi(skel->progs.umulti_run, pid, + "/proc/self/exe", NULL, &opts); + if (!ASSERT_OK_PTR(skel->links.umulti_run, "bpf_program__attach_uprobe_multi")) + goto out; + + link_fd = bpf_link__fd(skel->links.umulti_run); + if (invalid) + verify_umulti_invalid_user_buffer(link_fd); + else + verify_umulti_link_info(link_fd, retprobe, pid, offsets, cookies, ref_ctr_offsets); + + LINK_DESTROY(skel->links.umulti_run); + +out: + free(ref_ctr_offsets); + free(offsets); +} + void test_fill_link_info(void) { struct test_fill_link_info *skel; @@ -342,6 +524,13 @@ void test_fill_link_info(void) if (test__start_subtest("kprobe_multi_invalid_ubuff")) test_kprobe_multi_fill_link_info(skel, true, true); + if (test__start_subtest("uprobe_multi_link_info")) + test_uprobe_multi_fill_link_info(skel, false, false, -1); + if (test__start_subtest("uretprobe_multi_link_info")) + test_uprobe_multi_fill_link_info(skel, true, false, 0); + if (test__start_subtest("uprobe_multi_invalid")) + test_uprobe_multi_fill_link_info(skel, false, true, -1); + cleanup: test_fill_link_info__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/test_fill_link_info.c b/tools/testing/selftests/bpf/progs/test_fill_link_info.c index 564f402d56fe..69509f8bb680 100644 --- a/tools/testing/selftests/bpf/progs/test_fill_link_info.c +++ b/tools/testing/selftests/bpf/progs/test_fill_link_info.c @@ -39,4 +39,10 @@ int BPF_PROG(kmulti_run) return 0; } +SEC("uprobe.multi") +int BPF_PROG(umulti_run) +{ + return 0; +} + char _license[] SEC("license") = "GPL"; -- 2.41.0