Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: Add unique_match option for multi kprobe

Jordan reported an issue in Meta production environment where func
try_to_wake_up() is renamed to try_to_wake_up.llvm.<hash>() by clang
compiler at lto mode. The original 'kprobe/try_to_wake_up' does not
work any more since try_to_wake_up() does not match the actual func
name in /proc/kallsyms.

There are a couple of ways to resolve this issue. For example, in
attach_kprobe(), we could do lookup in /proc/kallsyms so try_to_wake_up()
can be replaced by try_to_wake_up.llvm.<hach>(). Or we can force users
to use bpf_program__attach_kprobe() where they need to lookup
/proc/kallsyms to find out try_to_wake_up.llvm.<hach>(). But these two
approaches requires extra work by either libbpf or user.

Luckily, suggested by Andrii, multi kprobe already supports wildcard ('*')
for symbol matching. In the above example, 'try_to_wake_up*' can match
to try_to_wake_up() or try_to_wake_up.llvm.<hash>() and this allows
bpf prog works for different kernels as some kernels may have
try_to_wake_up() and some others may have try_to_wake_up.llvm.<hash>().

The original intention is to kprobe try_to_wake_up() only, so an optional
field unique_match is added to struct bpf_kprobe_multi_opts. If the
field is set to true, the number of matched functions must be one.
Otherwise, the attachment will fail. In the above case, multi kprobe
with 'try_to_wake_up*' and unique_match preserves user functionality.

Reported-by: Jordan Rome <linux@jordanrome.com>
Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20250109174023.3368432-1-yonghong.song@linux.dev

authored by

Yonghong Song and committed by
Andrii Nakryiko
e2b0bda6 e8ec1c94

+15 -2
+12 -1
tools/lib/bpf/libbpf.c
··· 11534 11534 struct bpf_link *link = NULL; 11535 11535 const unsigned long *addrs; 11536 11536 int err, link_fd, prog_fd; 11537 - bool retprobe, session; 11537 + bool retprobe, session, unique_match; 11538 11538 const __u64 *cookies; 11539 11539 const char **syms; 11540 11540 size_t cnt; ··· 11553 11553 addrs = OPTS_GET(opts, addrs, false); 11554 11554 cnt = OPTS_GET(opts, cnt, false); 11555 11555 cookies = OPTS_GET(opts, cookies, false); 11556 + unique_match = OPTS_GET(opts, unique_match, false); 11556 11557 11557 11558 if (!pattern && !addrs && !syms) 11558 11559 return libbpf_err_ptr(-EINVAL); 11559 11560 if (pattern && (addrs || syms || cookies || cnt)) 11560 11561 return libbpf_err_ptr(-EINVAL); 11561 11562 if (!pattern && !cnt) 11563 + return libbpf_err_ptr(-EINVAL); 11564 + if (!pattern && unique_match) 11562 11565 return libbpf_err_ptr(-EINVAL); 11563 11566 if (addrs && syms) 11564 11567 return libbpf_err_ptr(-EINVAL); ··· 11573 11570 err = libbpf_available_kallsyms_parse(&res); 11574 11571 if (err) 11575 11572 goto error; 11573 + 11574 + if (unique_match && res.cnt != 1) { 11575 + pr_warn("prog '%s': failed to find a unique match for '%s' (%zu matches)\n", 11576 + prog->name, pattern, res.cnt); 11577 + err = -EINVAL; 11578 + goto error; 11579 + } 11580 + 11576 11581 addrs = res.addrs; 11577 11582 cnt = res.cnt; 11578 11583 }
+3 -1
tools/lib/bpf/libbpf.h
··· 552 552 bool retprobe; 553 553 /* create session kprobes */ 554 554 bool session; 555 + /* enforce unique match */ 556 + bool unique_match; 555 557 size_t :0; 556 558 }; 557 559 558 - #define bpf_kprobe_multi_opts__last_field session 560 + #define bpf_kprobe_multi_opts__last_field unique_match 559 561 560 562 LIBBPF_API struct bpf_link * 561 563 bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog,