Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpf: Add link_info support for uprobe multi link #5997

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -6562,6 +6562,16 @@ struct bpf_link_info {
__u32 flags;
__u64 missed;
} kprobe_multi;
struct {
__aligned_u64 path;
__aligned_u64 offsets;
__aligned_u64 ref_ctr_offsets;
__aligned_u64 cookies;
__u32 path_size; /* in/out: real path size on success */
__u32 count; /* in/out: uprobe_multi offsets/ref_ctr_offsets/cookies count */
__u32 flags;
__u32 pid;
} uprobe_multi;
struct {
__u32 type; /* enum bpf_perf_event_type */
__u32 :32;
Expand Down
86 changes: 75 additions & 11 deletions kernel/trace/bpf_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -3033,6 +3033,7 @@ struct bpf_uprobe_multi_link;
struct bpf_uprobe {
struct bpf_uprobe_multi_link *link;
loff_t offset;
unsigned long ref_ctr_offset;
u64 cookie;
struct uprobe_consumer consumer;
};
Expand All @@ -3043,6 +3044,7 @@ struct bpf_uprobe_multi_link {
u32 cnt;
struct bpf_uprobe *uprobes;
struct task_struct *task;
u32 flags;
};

struct bpf_uprobe_multi_run_ctx {
Expand Down Expand Up @@ -3082,9 +3084,79 @@ static void bpf_uprobe_multi_link_dealloc(struct bpf_link *link)
kfree(umulti_link);
}

static int bpf_uprobe_multi_link_fill_link_info(const struct bpf_link *link,
struct bpf_link_info *info)
{
u64 __user *uref_ctr_offsets = u64_to_user_ptr(info->uprobe_multi.ref_ctr_offsets);
u64 __user *ucookies = u64_to_user_ptr(info->uprobe_multi.cookies);
u64 __user *uoffsets = u64_to_user_ptr(info->uprobe_multi.offsets);
u64 __user *upath = u64_to_user_ptr(info->uprobe_multi.path);
u32 upath_size = info->uprobe_multi.path_size;
struct bpf_uprobe_multi_link *umulti_link;
u32 ucount = info->uprobe_multi.count;
int err = 0, i;
long left;

if (!upath ^ !upath_size)
return -EINVAL;

if ((uoffsets || uref_ctr_offsets || ucookies) && !ucount)
return -EINVAL;

umulti_link = container_of(link, struct bpf_uprobe_multi_link, link);
info->uprobe_multi.count = umulti_link->cnt;
info->uprobe_multi.flags = umulti_link->flags;
info->uprobe_multi.pid = umulti_link->task ?
task_pid_nr_ns(umulti_link->task, task_active_pid_ns(current)) : 0;

if (upath) {
char *p, *buf;

upath_size = min_t(u32, upath_size, PATH_MAX);

buf = kmalloc(upath_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
p = d_path(&umulti_link->path, buf, upath_size);
if (IS_ERR(p)) {
kfree(buf);
return -ENOSPC;
}
upath_size = buf + upath_size - p;
left = copy_to_user(upath, p, upath_size);
kfree(buf);
if (left)
return -EFAULT;
info->uprobe_multi.path_size = upath_size - 1 /* NULL */;
}

if (!uoffsets && !ucookies && !uref_ctr_offsets)
return 0;

if (ucount < umulti_link->cnt)
err = -ENOSPC;
else
ucount = umulti_link->cnt;

for (i = 0; i < ucount; i++) {
if (uoffsets &&
put_user(umulti_link->uprobes[i].offset, uoffsets + i))
return -EFAULT;
if (uref_ctr_offsets &&
put_user(umulti_link->uprobes[i].ref_ctr_offset, uref_ctr_offsets + i))
return -EFAULT;
if (ucookies &&
put_user(umulti_link->uprobes[i].cookie, ucookies + i))
return -EFAULT;
}

return err;
}

static const struct bpf_link_ops bpf_uprobe_multi_link_lops = {
.release = bpf_uprobe_multi_link_release,
.dealloc = bpf_uprobe_multi_link_dealloc,
.fill_link_info = bpf_uprobe_multi_link_fill_link_info,
};

static int uprobe_prog_run(struct bpf_uprobe *uprobe,
Expand Down Expand Up @@ -3172,7 +3244,6 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
{
struct bpf_uprobe_multi_link *link = NULL;
unsigned long __user *uref_ctr_offsets;
unsigned long *ref_ctr_offsets = NULL;
struct bpf_link_primer link_primer;
struct bpf_uprobe *uprobes = NULL;
struct task_struct *task = NULL;
Expand Down Expand Up @@ -3245,18 +3316,12 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
if (!uprobes || !link)
goto error_free;

if (uref_ctr_offsets) {
ref_ctr_offsets = kvcalloc(cnt, sizeof(*ref_ctr_offsets), GFP_KERNEL);
if (!ref_ctr_offsets)
goto error_free;
}

for (i = 0; i < cnt; i++) {
if (ucookies && __get_user(uprobes[i].cookie, ucookies + i)) {
err = -EFAULT;
goto error_free;
}
if (uref_ctr_offsets && __get_user(ref_ctr_offsets[i], uref_ctr_offsets + i)) {
if (uref_ctr_offsets && __get_user(uprobes[i].ref_ctr_offset, uref_ctr_offsets + i)) {
err = -EFAULT;
goto error_free;
}
Expand All @@ -3280,14 +3345,15 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
link->uprobes = uprobes;
link->path = path;
link->task = task;
link->flags = flags;

bpf_link_init(&link->link, BPF_LINK_TYPE_UPROBE_MULTI,
&bpf_uprobe_multi_link_lops, prog);

for (i = 0; i < cnt; i++) {
err = uprobe_register_refctr(d_real_inode(link->path.dentry),
uprobes[i].offset,
ref_ctr_offsets ? ref_ctr_offsets[i] : 0,
uprobes[i].ref_ctr_offset,
&uprobes[i].consumer);
if (err) {
bpf_uprobe_unregister(&path, uprobes, i);
Expand All @@ -3299,11 +3365,9 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
if (err)
goto error_free;

kvfree(ref_ctr_offsets);
return bpf_link_settle(&link_primer);

error_free:
kvfree(ref_ctr_offsets);
kvfree(uprobes);
kfree(link);
if (task)
Expand Down
105 changes: 103 additions & 2 deletions tools/bpf/bpftool/link.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,37 @@ show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
jsonw_end_array(json_wtr);
}

static __u64 *u64_to_arr(__u64 val)
{
return (__u64 *) u64_to_ptr(val);
}

static void
show_uprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
{
__u32 i;

jsonw_bool_field(json_wtr, "retprobe",
info->uprobe_multi.flags & BPF_F_UPROBE_MULTI_RETURN);
jsonw_string_field(json_wtr, "path", (char *) u64_to_ptr(info->uprobe_multi.path));
jsonw_uint_field(json_wtr, "func_cnt", info->uprobe_multi.count);
jsonw_int_field(json_wtr, "pid", (int) info->uprobe_multi.pid);
jsonw_name(json_wtr, "funcs");
jsonw_start_array(json_wtr);

for (i = 0; i < info->uprobe_multi.count; i++) {
jsonw_start_object(json_wtr);
jsonw_uint_field(json_wtr, "offset",
u64_to_arr(info->uprobe_multi.offsets)[i]);
jsonw_uint_field(json_wtr, "ref_ctr_offset",
u64_to_arr(info->uprobe_multi.ref_ctr_offsets)[i]);
jsonw_uint_field(json_wtr, "cookie",
u64_to_arr(info->uprobe_multi.cookies)[i]);
jsonw_end_object(json_wtr);
}
jsonw_end_array(json_wtr);
}

static void
show_perf_event_kprobe_json(struct bpf_link_info *info, json_writer_t *wtr)
{
Expand Down Expand Up @@ -465,6 +496,9 @@ static int show_link_close_json(int fd, struct bpf_link_info *info)
case BPF_LINK_TYPE_KPROBE_MULTI:
show_kprobe_multi_json(info, json_wtr);
break;
case BPF_LINK_TYPE_UPROBE_MULTI:
show_uprobe_multi_json(info, json_wtr);
break;
case BPF_LINK_TYPE_PERF_EVENT:
switch (info->perf_event.type) {
case BPF_PERF_EVENT_EVENT:
Expand Down Expand Up @@ -674,6 +708,33 @@ static void show_kprobe_multi_plain(struct bpf_link_info *info)
}
}

static void show_uprobe_multi_plain(struct bpf_link_info *info)
{
__u32 i;

if (!info->uprobe_multi.count)
return;

if (info->uprobe_multi.flags & BPF_F_UPROBE_MULTI_RETURN)
printf("\n\turetprobe.multi ");
else
printf("\n\tuprobe.multi ");

printf("path %s ", (char *) u64_to_ptr(info->uprobe_multi.path));
printf("func_cnt %u ", info->uprobe_multi.count);

if (info->uprobe_multi.pid != (__u32) -1)
printf("pid %d ", info->uprobe_multi.pid);

printf("\n\t%-16s %-16s %-16s", "offset", "ref_ctr_offset", "cookies");
for (i = 0; i < info->uprobe_multi.count; i++) {
printf("\n\t0x%-16llx 0x%-16llx 0x%-16llx",
u64_to_arr(info->uprobe_multi.offsets)[i],
u64_to_arr(info->uprobe_multi.ref_ctr_offsets)[i],
u64_to_arr(info->uprobe_multi.cookies)[i]);
}
}

static void show_perf_event_kprobe_plain(struct bpf_link_info *info)
{
const char *buf;
Expand Down Expand Up @@ -807,6 +868,9 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)
case BPF_LINK_TYPE_KPROBE_MULTI:
show_kprobe_multi_plain(info);
break;
case BPF_LINK_TYPE_UPROBE_MULTI:
show_uprobe_multi_plain(info);
break;
case BPF_LINK_TYPE_PERF_EVENT:
switch (info->perf_event.type) {
case BPF_PERF_EVENT_EVENT:
Expand Down Expand Up @@ -846,8 +910,10 @@ static int show_link_close_plain(int fd, struct bpf_link_info *info)

static int do_show_link(int fd)
{
__u64 *ref_ctr_offsets = NULL, *offsets = NULL, *cookies = NULL;
struct bpf_link_info info;
__u32 len = sizeof(info);
char path_buf[PATH_MAX];
__u64 *addrs = NULL;
char buf[PATH_MAX];
int count;
Expand Down Expand Up @@ -889,6 +955,39 @@ static int do_show_link(int fd)
goto again;
}
}
if (info.type == BPF_LINK_TYPE_UPROBE_MULTI &&
!info.uprobe_multi.offsets) {
count = info.uprobe_multi.count;
if (count) {
offsets = calloc(count, sizeof(__u64));
if (!offsets) {
p_err("mem alloc failed");
close(fd);
return -ENOMEM;
}
info.uprobe_multi.offsets = ptr_to_u64(offsets);
ref_ctr_offsets = calloc(count, sizeof(__u64));
if (!ref_ctr_offsets) {
p_err("mem alloc failed");
free(offsets);
close(fd);
return -ENOMEM;
}
info.uprobe_multi.ref_ctr_offsets = ptr_to_u64(ref_ctr_offsets);
cookies = calloc(count, sizeof(__u64));
if (!cookies) {
p_err("mem alloc failed");
free(cookies);
free(offsets);
close(fd);
return -ENOMEM;
}
info.uprobe_multi.cookies = ptr_to_u64(cookies);
info.uprobe_multi.path = ptr_to_u64(path_buf);
info.uprobe_multi.path_size = sizeof(path_buf);
goto again;
}
}
if (info.type == BPF_LINK_TYPE_PERF_EVENT) {
switch (info.perf_event.type) {
case BPF_PERF_EVENT_TRACEPOINT:
Expand Down Expand Up @@ -924,8 +1023,10 @@ static int do_show_link(int fd)
else
show_link_close_plain(fd, &info);

if (addrs)
free(addrs);
free(ref_ctr_offsets);
free(cookies);
free(offsets);
free(addrs);
close(fd);
return 0;
}
Expand Down
10 changes: 10 additions & 0 deletions tools/include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -6562,6 +6562,16 @@ struct bpf_link_info {
__u32 flags;
__u64 missed;
} kprobe_multi;
struct {
__aligned_u64 path;
__aligned_u64 offsets;
__aligned_u64 ref_ctr_offsets;
__aligned_u64 cookies;
__u32 path_size; /* in/out: real path size on success */
__u32 count; /* in/out: uprobe_multi offsets/ref_ctr_offsets/cookies count */
__u32 flags;
__u32 pid;
} uprobe_multi;
struct {
__u32 type; /* enum bpf_perf_event_type */
__u32 :32;
Expand Down
5 changes: 3 additions & 2 deletions tools/lib/bpf/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,8 @@ static int symbol_cmp(const void *a, const void *b)
* size, that needs to be released by the caller.
*/
int elf_resolve_syms_offsets(const char *binary_path, int cnt,
const char **syms, unsigned long **poffsets)
const char **syms, unsigned long **poffsets,
int st_type)
{
int sh_types[2] = { SHT_DYNSYM, SHT_SYMTAB };
int err = 0, i, cnt_done = 0;
Expand Down Expand Up @@ -438,7 +439,7 @@ int elf_resolve_syms_offsets(const char *binary_path, int cnt,
struct elf_sym_iter iter;
struct elf_sym *sym;

err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], STT_FUNC);
err = elf_sym_iter_new(&iter, elf_fd.elf, binary_path, sh_types[i], st_type);
if (err == -ENOENT)
continue;
if (err)
Expand Down
2 changes: 1 addition & 1 deletion tools/lib/bpf/libbpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -11447,7 +11447,7 @@ bpf_program__attach_uprobe_multi(const struct bpf_program *prog,
return libbpf_err_ptr(err);
offsets = resolved_offsets;
} else if (syms) {
err = elf_resolve_syms_offsets(path, cnt, syms, &resolved_offsets);
err = elf_resolve_syms_offsets(path, cnt, syms, &resolved_offsets, STT_FUNC);
if (err < 0)
return libbpf_err_ptr(err);
offsets = resolved_offsets;
Expand Down
3 changes: 2 additions & 1 deletion tools/lib/bpf/libbpf_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,8 @@ int elf_open(const char *binary_path, struct elf_fd *elf_fd);
void elf_close(struct elf_fd *elf_fd);

int elf_resolve_syms_offsets(const char *binary_path, int cnt,
const char **syms, unsigned long **poffsets);
const char **syms, unsigned long **poffsets,
int st_type);
int elf_resolve_pattern_offsets(const char *binary_path, const char *pattern,
unsigned long **poffsets, size_t *pcnt);

Expand Down
Loading
Loading