diff mbox series

[PATCHv2,bpf-next,3/6] bpf: Add link_info support for uprobe multi link

Message ID 20231109092838.721233-4-jolsa@kernel.org (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series bpf: Add link_info support for uprobe multi link | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 3106 this patch: 3106
netdev/cc_maintainers warning 7 maintainers not CCed: rostedt@goodmis.org song@kernel.org mhiramat@kernel.org yonghong.song@linux.dev martin.lau@linux.dev linux-trace-kernel@vger.kernel.org kpsingh@kernel.org
netdev/build_clang success Errors and warnings before: 1530 this patch: 1530
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 3191 this patch: 3191
netdev/checkpatch warning WARNING: line length of 100 exceeds 80 columns WARNING: line length of 101 exceeds 80 columns WARNING: line length of 91 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-3 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-10 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-12 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-9 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-15 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-16 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 fail Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-llvm-16 / build / build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-llvm-16 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-llvm-16 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-llvm-16 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-16 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-16 / veristat

Commit Message

Jiri Olsa Nov. 9, 2023, 9:28 a.m. UTC
Adding support to get uprobe_link details through bpf_link_info
interface.

Adding new struct uprobe_multi to struct bpf_link_info to carry
the uprobe_multi link details.

The uprobe_multi.count is passed from user space to denote size
of array fields (offsets/ref_ctr_offsets/cookies). The actual
array size is stored back to uprobe_multi.count (allowing user
to find out the actual array size) and array fields are populated
up to the user passed size.

All the non-array fields (path/count/flags/pid) are always set.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 include/uapi/linux/bpf.h       | 10 +++++
 kernel/trace/bpf_trace.c       | 69 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 10 +++++
 3 files changed, 89 insertions(+)

Comments

Andrii Nakryiko Nov. 10, 2023, 5:57 a.m. UTC | #1
On Thu, Nov 9, 2023 at 1:29 AM Jiri Olsa <jolsa@kernel.org> wrote:
>
> Adding support to get uprobe_link details through bpf_link_info
> interface.
>
> Adding new struct uprobe_multi to struct bpf_link_info to carry
> the uprobe_multi link details.
>
> The uprobe_multi.count is passed from user space to denote size
> of array fields (offsets/ref_ctr_offsets/cookies). The actual
> array size is stored back to uprobe_multi.count (allowing user
> to find out the actual array size) and array fields are populated
> up to the user passed size.
>
> All the non-array fields (path/count/flags/pid) are always set.
>
> Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> ---
>  include/uapi/linux/bpf.h       | 10 +++++
>  kernel/trace/bpf_trace.c       | 69 ++++++++++++++++++++++++++++++++++
>  tools/include/uapi/linux/bpf.h | 10 +++++
>  3 files changed, 89 insertions(+)
>
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 0f6cdf52b1da..05b355da4508 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -6556,6 +6556,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;
> +                       __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;
> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index 52c1ec3a0467..1ea54f3b3f73 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -3046,6 +3046,7 @@ struct bpf_uprobe_multi_link {
>         u32 cnt;
>         struct bpf_uprobe *uprobes;
>         struct task_struct *task;
> +       u32 flags;
>  };
>
>  struct bpf_uprobe_multi_run_ctx {
> @@ -3085,9 +3086,76 @@ 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 ^ !ucount)

uoffsets is not the only one that requires ucount, right?

> +               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;
> +               }
> +               left = copy_to_user(upath, p, buf + upath_size - p);
> +               kfree(buf);
> +               if (left)
> +                       return -EFAULT;

hmm.. I expected the actual path_size to be reported back to the
user?.. Is there a problem with doing that?

> +       }
> +
> +       if (!uoffsets)
> +               return 0;

why guard by uoffsets? what if users only wanted cookies? I think each
array should do its own checking and be independent, no?

> +
> +       if (ucount < umulti_link->cnt)
> +               err = -ENOSPC;
> +       else
> +               ucount = umulti_link->cnt;
> +
> +       for (i = 0; i < ucount; i++) {
> +               if (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,
> @@ -3276,6 +3344,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);
> diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> index 0f6cdf52b1da..05b355da4508 100644
> --- a/tools/include/uapi/linux/bpf.h
> +++ b/tools/include/uapi/linux/bpf.h
> @@ -6556,6 +6556,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;
> +                       __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;
> --
> 2.41.0
>
Jiri Olsa Nov. 10, 2023, 9:01 a.m. UTC | #2
On Thu, Nov 09, 2023 at 09:57:03PM -0800, Andrii Nakryiko wrote:

SNIP

> > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> > index 52c1ec3a0467..1ea54f3b3f73 100644
> > --- a/kernel/trace/bpf_trace.c
> > +++ b/kernel/trace/bpf_trace.c
> > @@ -3046,6 +3046,7 @@ struct bpf_uprobe_multi_link {
> >         u32 cnt;
> >         struct bpf_uprobe *uprobes;
> >         struct task_struct *task;
> > +       u32 flags;
> >  };
> >
> >  struct bpf_uprobe_multi_run_ctx {
> > @@ -3085,9 +3086,76 @@ 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 ^ !ucount)
> 
> uoffsets is not the only one that requires ucount, right?

yep, cookies as well

> 
> > +               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;
> > +               }
> > +               left = copy_to_user(upath, p, buf + upath_size - p);
> > +               kfree(buf);
> > +               if (left)
> > +                       return -EFAULT;
> 
> hmm.. I expected the actual path_size to be reported back to the
> user?.. Is there a problem with doing that?

we return back the string, if the string fits in provided buffer it's
terminated with 0 and user space can do strlen on it if needed

> 
> > +       }
> > +
> > +       if (!uoffsets)
> > +               return 0;
> 
> why guard by uoffsets? what if users only wanted cookies? I think each
> array should do its own checking and be independent, no?

I did not think of the use case to get just the cookies (at least not the
one in bpftool), I saw it as optional to offsets, which is mandatory..
but that should be an easy change I think

jirka

> 
> > +
> > +       if (ucount < umulti_link->cnt)
> > +               err = -ENOSPC;
> > +       else
> > +               ucount = umulti_link->cnt;
> > +
> > +       for (i = 0; i < ucount; i++) {
> > +               if (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,
> > @@ -3276,6 +3344,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);
> > diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> > index 0f6cdf52b1da..05b355da4508 100644
> > --- a/tools/include/uapi/linux/bpf.h
> > +++ b/tools/include/uapi/linux/bpf.h
> > @@ -6556,6 +6556,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;
> > +                       __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;
> > --
> > 2.41.0
> >
Andrii Nakryiko Nov. 10, 2023, 5:24 p.m. UTC | #3
On Fri, Nov 10, 2023 at 1:01 AM Jiri Olsa <olsajiri@gmail.com> wrote:
>
> On Thu, Nov 09, 2023 at 09:57:03PM -0800, Andrii Nakryiko wrote:
>
> SNIP
>
> > > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> > > index 52c1ec3a0467..1ea54f3b3f73 100644
> > > --- a/kernel/trace/bpf_trace.c
> > > +++ b/kernel/trace/bpf_trace.c
> > > @@ -3046,6 +3046,7 @@ struct bpf_uprobe_multi_link {
> > >         u32 cnt;
> > >         struct bpf_uprobe *uprobes;
> > >         struct task_struct *task;
> > > +       u32 flags;
> > >  };
> > >
> > >  struct bpf_uprobe_multi_run_ctx {
> > > @@ -3085,9 +3086,76 @@ 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 ^ !ucount)
> >
> > uoffsets is not the only one that requires ucount, right?
>
> yep, cookies as well

so I think all those arrays should be treated as completely
independent and optional. So if any of ref_ctr_offsets, cookies, or
offsets are requested, then count should be non-zero.

>
> >
> > > +               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;
> > > +               }
> > > +               left = copy_to_user(upath, p, buf + upath_size - p);
> > > +               kfree(buf);
> > > +               if (left)
> > > +                       return -EFAULT;
> >
> > hmm.. I expected the actual path_size to be reported back to the
> > user?.. Is there a problem with doing that?
>
> we return back the string, if the string fits in provided buffer it's
> terminated with 0 and user space can do strlen on it if needed

sure, but we can also specify the exact size. We know if, what's the
problem with that? It's just basically saying that path_size is in/out
parameter, just like count

>
> >
> > > +       }
> > > +
> > > +       if (!uoffsets)
> > > +               return 0;
> >
> > why guard by uoffsets? what if users only wanted cookies? I think each
> > array should do its own checking and be independent, no?
>
> I did not think of the use case to get just the cookies (at least not the
> one in bpftool), I saw it as optional to offsets, which is mandatory..
> but that should be an easy change I think

yeah, let's not bake in any assumptions. Each array is optional, user
should be able to request any or all of them. Having this dependency
on offsets is confusing from user POV.

>
> jirka
>
> >
> > > +
> > > +       if (ucount < umulti_link->cnt)
> > > +               err = -ENOSPC;
> > > +       else
> > > +               ucount = umulti_link->cnt;
> > > +
> > > +       for (i = 0; i < ucount; i++) {
> > > +               if (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,
> > > @@ -3276,6 +3344,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);
> > > diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> > > index 0f6cdf52b1da..05b355da4508 100644
> > > --- a/tools/include/uapi/linux/bpf.h
> > > +++ b/tools/include/uapi/linux/bpf.h
> > > @@ -6556,6 +6556,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;
> > > +                       __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;
> > > --
> > > 2.41.0
> > >
diff mbox series

Patch

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 0f6cdf52b1da..05b355da4508 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -6556,6 +6556,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;
+			__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;
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 52c1ec3a0467..1ea54f3b3f73 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -3046,6 +3046,7 @@  struct bpf_uprobe_multi_link {
 	u32 cnt;
 	struct bpf_uprobe *uprobes;
 	struct task_struct *task;
+	u32 flags;
 };
 
 struct bpf_uprobe_multi_run_ctx {
@@ -3085,9 +3086,76 @@  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 ^ !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;
+		}
+		left = copy_to_user(upath, p, buf + upath_size - p);
+		kfree(buf);
+		if (left)
+			return -EFAULT;
+	}
+
+	if (!uoffsets)
+		return 0;
+
+	if (ucount < umulti_link->cnt)
+		err = -ENOSPC;
+	else
+		ucount = umulti_link->cnt;
+
+	for (i = 0; i < ucount; i++) {
+		if (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,
@@ -3276,6 +3344,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);
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 0f6cdf52b1da..05b355da4508 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -6556,6 +6556,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;
+			__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;