Commit d4e7dd48 authored by Andrii Nakryiko's avatar Andrii Nakryiko
Browse files

Merge branch 'bpf-add-link_info-support-for-uprobe-multi-link'

Jiri Olsa says:

====================
bpf: Add link_info support for uprobe multi link

hi,
this patchset adds support to get bpf_link_info details for
uprobe_multi links and adding support for bpftool link to
display them.

v4 changes:
  - move flags field up in bpf_uprobe_multi_link [Andrii]
  - include zero terminating byte in path_size [Andrii]
  - return d_path error directly [Yonghong]
  - use SEC(".probes") for semaphores [Yonghong]
  - fix ref_ctr_offsets leak in test [Yonghong]
  - other smaller fixes [Yonghong]

thanks,
jirka
---
====================

Link: https://lore.kernel.org/r/20231125193130.834322-1-jolsa@kernel.org


Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents cf979163 a7795698
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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, including zero byte */
			__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;
+75 −11
Original line number Diff line number Diff line
@@ -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;
};
@@ -3041,6 +3042,7 @@ struct bpf_uprobe_multi_link {
	struct path path;
	struct bpf_link link;
	u32 cnt;
	u32 flags;
	struct bpf_uprobe *uprobes;
	struct task_struct *task;
};
@@ -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 PTR_ERR(p);
		}
		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;
	}

	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,
@@ -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;
@@ -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;
		}
@@ -3280,6 +3345,7 @@ 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);
@@ -3287,7 +3353,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
	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);
@@ -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)
+103 −2
Original line number Diff line number Diff line
@@ -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)
{
@@ -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:
@@ -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)
		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;
@@ -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:
@@ -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;
@@ -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:
@@ -924,7 +1023,9 @@ static int do_show_link(int fd)
	else
		show_link_close_plain(fd, &info);

	if (addrs)
	free(ref_ctr_offsets);
	free(cookies);
	free(offsets);
	free(addrs);
	close(fd);
	return 0;
+10 −0
Original line number Diff line number Diff line
@@ -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, including zero byte */
			__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;
+3 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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)
Loading