Skip to content
Open
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
7 changes: 6 additions & 1 deletion include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1327,7 +1327,11 @@ enum {
* BPF_TRACE_UPROBE_MULTI attach type to create return probe.
*/
enum {
BPF_F_UPROBE_MULTI_RETURN = (1U << 0)
/* Get return uprobe. */
BPF_F_UPROBE_MULTI_RETURN = (1U << 0),

/* Get path from provided path_fd. */
BPF_F_UPROBE_MULTI_PATH_FD = (1U << 1),
};

/* link_create.netfilter.flags used in LINK_CREATE command for
Expand Down Expand Up @@ -1864,6 +1868,7 @@ union bpf_attr {
__u32 cnt;
__u32 flags;
__u32 pid;
__u32 path_fd;
} uprobe_multi;
struct {
union {
Expand Down
4 changes: 2 additions & 2 deletions kernel/bpf/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -3480,7 +3480,7 @@ static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp)
seq_printf(m, "link_type:\t%s\n", link->flags == BPF_F_KPROBE_MULTI_RETURN ?
"kretprobe_multi" : "kprobe_multi");
else if (link->type == BPF_LINK_TYPE_UPROBE_MULTI)
seq_printf(m, "link_type:\t%s\n", link->flags == BPF_F_UPROBE_MULTI_RETURN ?
seq_printf(m, "link_type:\t%s\n", link->flags & BPF_F_UPROBE_MULTI_RETURN ?
"uretprobe_multi" : "uprobe_multi");
else
seq_printf(m, "link_type:\t%s\n", bpf_link_type_strs[type]);
Expand Down Expand Up @@ -5840,7 +5840,7 @@ static int bpf_map_do_batch(const union bpf_attr *attr,
return err;
}

#define BPF_LINK_CREATE_LAST_FIELD link_create.uprobe_multi.pid
#define BPF_LINK_CREATE_LAST_FIELD link_create.uprobe_multi.path_fd
static int link_create(union bpf_attr *attr, bpfptr_t uattr)
{
struct bpf_prog *prog;
Expand Down
51 changes: 37 additions & 14 deletions kernel/trace/bpf_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <linux/sort.h>
#include <linux/key.h>
#include <linux/namei.h>
#include <linux/file.h>

#include <net/bpf_sk_storage.h>

Expand Down Expand Up @@ -3214,6 +3215,38 @@ static u64 bpf_uprobe_multi_cookie(struct bpf_run_ctx *ctx)
return run_ctx->uprobe->cookie;
}

static int bpf_uprobe_multi_get_path(const union bpf_attr *attr, struct path *path)
{
void __user *upath = u64_to_user_ptr(attr->link_create.uprobe_multi.path);
u32 path_fd = attr->link_create.uprobe_multi.path_fd;
u32 flags = attr->link_create.uprobe_multi.flags;

if (flags & BPF_F_UPROBE_MULTI_PATH_FD) {
/*
* When BPF_F_UPROBE_MULTI_PATH_FD is set, the executable is
* identified by path_fd, upath must be NULL.
*/
if (upath)
return -EINVAL;

CLASS(fd, f)(path_fd);
if (fd_empty(f))
return -EBADF;
*path = fd_file(f)->f_path;
path_get(path);
return 0;
}

/*
* When BPF_F_UPROBE_MULTI_PATH_FD is not set, the path is resolved
* relative to the cwd (AT_FDCWD) or absolute using the upath string.
*/
if (!upath || path_fd)
return -EINVAL;

return user_path_at(AT_FDCWD, upath, LOOKUP_FOLLOW, path);
}

int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
{
struct bpf_uprobe_multi_link *link = NULL;
Expand All @@ -3223,10 +3256,8 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
struct task_struct *task = NULL;
unsigned long __user *uoffsets;
u64 __user *ucookies;
void __user *upath;
u32 flags, cnt, i;
struct path path;
char *name;
pid_t pid;
int err;

Expand All @@ -3241,34 +3272,26 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
return -EINVAL;

flags = attr->link_create.uprobe_multi.flags;
if (flags & ~BPF_F_UPROBE_MULTI_RETURN)
if (flags & ~(BPF_F_UPROBE_MULTI_RETURN | BPF_F_UPROBE_MULTI_PATH_FD))
return -EINVAL;

/*
* path, offsets and cnt are mandatory,
* offsets and cnt are mandatory,
* ref_ctr_offsets and cookies are optional
*/
upath = u64_to_user_ptr(attr->link_create.uprobe_multi.path);
uoffsets = u64_to_user_ptr(attr->link_create.uprobe_multi.offsets);
cnt = attr->link_create.uprobe_multi.cnt;
pid = attr->link_create.uprobe_multi.pid;

if (!upath || !uoffsets || !cnt || pid < 0)
if (!uoffsets || !cnt || pid < 0)
return -EINVAL;
if (cnt > MAX_UPROBE_MULTI_CNT)
return -E2BIG;

uref_ctr_offsets = u64_to_user_ptr(attr->link_create.uprobe_multi.ref_ctr_offsets);
ucookies = u64_to_user_ptr(attr->link_create.uprobe_multi.cookies);

name = strndup_user(upath, PATH_MAX);
if (IS_ERR(name)) {
err = PTR_ERR(name);
return err;
}

err = kern_path(name, LOOKUP_FOLLOW, &path);
kfree(name);
err = bpf_uprobe_multi_get_path(attr, &path);
if (err)
return err;

Expand Down
7 changes: 6 additions & 1 deletion tools/include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1327,7 +1327,11 @@ enum {
* BPF_TRACE_UPROBE_MULTI attach type to create return probe.
*/
enum {
BPF_F_UPROBE_MULTI_RETURN = (1U << 0)
/* Get return uprobe. */
BPF_F_UPROBE_MULTI_RETURN = (1U << 0),

/* Get path from provided path_fd. */
BPF_F_UPROBE_MULTI_PATH_FD = (1U << 1),
};

/* link_create.netfilter.flags used in LINK_CREATE command for
Expand Down Expand Up @@ -1864,6 +1868,7 @@ union bpf_attr {
__u32 cnt;
__u32 flags;
__u32 pid;
__u32 path_fd;
} uprobe_multi;
struct {
union {
Expand Down
1 change: 1 addition & 0 deletions tools/lib/bpf/bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,7 @@ int bpf_link_create(int prog_fd, int target_fd,
attr.link_create.uprobe_multi.ref_ctr_offsets = ptr_to_u64(OPTS_GET(opts, uprobe_multi.ref_ctr_offsets, 0));
attr.link_create.uprobe_multi.cookies = ptr_to_u64(OPTS_GET(opts, uprobe_multi.cookies, 0));
attr.link_create.uprobe_multi.pid = OPTS_GET(opts, uprobe_multi.pid, 0);
attr.link_create.uprobe_multi.path_fd = OPTS_GET(opts, uprobe_multi.path_fd, 0);
if (!OPTS_ZEROED(opts, uprobe_multi))
return libbpf_err(-EINVAL);
break;
Expand Down
3 changes: 2 additions & 1 deletion tools/lib/bpf/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ struct bpf_link_create_opts {
const unsigned long *ref_ctr_offsets;
const __u64 *cookies;
__u32 pid;
__u32 path_fd;
} uprobe_multi;
struct {
__u64 cookie;
Expand Down Expand Up @@ -477,7 +478,7 @@ struct bpf_link_create_opts {
};
size_t :0;
};
#define bpf_link_create_opts__last_field uprobe_multi.pid
#define bpf_link_create_opts__last_field uprobe_multi.path_fd

LIBBPF_API int bpf_link_create(int prog_fd, int target_fd,
enum bpf_attach_type attach_type,
Expand Down
2 changes: 1 addition & 1 deletion tools/testing/selftests/bpf/prog_tests/fill_link_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ verify_umulti_link_info(int fd, bool retprobe, __u64 *offsets,

ASSERT_EQ(info.uprobe_multi.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,
ASSERT_EQ(info.uprobe_multi.flags & BPF_F_UPROBE_MULTI_RETURN,
retprobe, "info.uprobe_multi.flags.retprobe");
ASSERT_EQ(info.uprobe_multi.path_size, strlen(path) + 1, "info.uprobe_multi.path_size");
ASSERT_STREQ(path_buf, path, "info.uprobe_multi.path");
Expand Down
94 changes: 93 additions & 1 deletion tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <test_progs.h>
#include "uprobe_multi.skel.h"
#include "uprobe_multi_bench.skel.h"
Expand Down Expand Up @@ -536,7 +537,37 @@ static void test_attach_api_fails(void)
link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
if (!ASSERT_ERR(link_fd, "link_fd"))
goto cleanup;
ASSERT_EQ(link_fd, -EINVAL, "pid_is_wrong");
if (!ASSERT_EQ(link_fd, -EINVAL, "pid_is_wrong"))
goto cleanup;

/* wrong path_fd */
LIBBPF_OPTS_RESET(opts,
.uprobe_multi.path = NULL,
.uprobe_multi.path_fd = -1,
.uprobe_multi.flags = BPF_F_UPROBE_MULTI_PATH_FD,
.uprobe_multi.offsets = (unsigned long *)&offset,
.uprobe_multi.cnt = 1,
);

link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
if (!ASSERT_ERR(link_fd, "link_fd"))
goto cleanup;
if (!ASSERT_EQ(link_fd, -EBADF, "path_fd_is_wrong"))
goto cleanup;

/* path and path_fd both set with BPF_F_UPROBE_MULTI_PATH_FD flag */
LIBBPF_OPTS_RESET(opts,
.uprobe_multi.path = path,
.uprobe_multi.path_fd = 1,
.uprobe_multi.flags = BPF_F_UPROBE_MULTI_PATH_FD,
.uprobe_multi.offsets = (unsigned long *)&offset,
.uprobe_multi.cnt = 1,
);

link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
if (!ASSERT_ERR(link_fd, "link_fd"))
goto cleanup;
ASSERT_EQ(link_fd, -EINVAL, "path_and_path_fd_together");

cleanup:
if (link_fd >= 0)
Expand Down Expand Up @@ -757,6 +788,65 @@ static void test_link_api(void)
__test_link_api(&child);
}

static void test_link_api_path_fd(void)
{
LIBBPF_OPTS(bpf_link_create_opts, opts);
const char *resolve_path = "/proc/self/exe";
int prog_fd, link_fd = -1, path_fd = -1;
struct uprobe_multi *skel = NULL;
unsigned long *offsets = NULL;
const char *syms[3] = {
"uprobe_multi_func_1",
"uprobe_multi_func_2",
"uprobe_multi_func_3",
};
int err;

err = elf_resolve_syms_offsets(resolve_path, ARRAY_SIZE(syms), syms,
&offsets, STT_FUNC);
if (!ASSERT_OK(err, "elf_resolve_syms_offsets"))
return;

path_fd = open(resolve_path, O_RDONLY);
if (!ASSERT_GE(path_fd, 0, "path_fd"))
goto cleanup;

opts.uprobe_multi.path_fd = path_fd;
opts.uprobe_multi.offsets = offsets;
opts.uprobe_multi.cnt = ARRAY_SIZE(syms);
opts.uprobe_multi.flags = BPF_F_UPROBE_MULTI_PATH_FD;

skel = uprobe_multi__open_and_load();
if (!ASSERT_OK_PTR(skel, "uprobe_multi__open_and_load"))
goto cleanup;

prog_fd = bpf_program__fd(skel->progs.uprobe);
link_fd = bpf_link_create(prog_fd, 0, BPF_TRACE_UPROBE_MULTI, &opts);
if (!ASSERT_GE(link_fd, 0, "bpf_link_create"))
goto cleanup;

skel->bss->uprobe_multi_func_1_addr = (__u64)uprobe_multi_func_1;
skel->bss->uprobe_multi_func_2_addr = (__u64)uprobe_multi_func_2;
skel->bss->uprobe_multi_func_3_addr = (__u64)uprobe_multi_func_3;
skel->bss->pid = getpid();

uprobe_multi_func_1();
uprobe_multi_func_2();
uprobe_multi_func_3();

ASSERT_EQ(skel->bss->uprobe_multi_func_1_result, 1, "uprobe_multi_func_1_result");
ASSERT_EQ(skel->bss->uprobe_multi_func_2_result, 1, "uprobe_multi_func_2_result");
ASSERT_EQ(skel->bss->uprobe_multi_func_3_result, 1, "uprobe_multi_func_3_result");

cleanup:
if (link_fd >= 0)
close(link_fd);
if (path_fd >= 0)
close(path_fd);
uprobe_multi__destroy(skel);
free(offsets);
}

static struct bpf_program *
get_program(struct uprobe_multi_consumers *skel, int prog)
{
Expand Down Expand Up @@ -1354,6 +1444,8 @@ void test_uprobe_multi_test(void)
test_attach_api_syms();
if (test__start_subtest("link_api"))
test_link_api();
if (test__start_subtest("link_api_path_fd"))
test_link_api_path_fd();
if (test__start_subtest("bench_uprobe"))
test_bench_attach_uprobe();
if (test__start_subtest("bench_usdt"))
Expand Down