* [PATCHv3 bpf-next 0/7] bpf: Allow uprobe_multi binary specified by file descriptor
@ 2026-06-11 11:42 Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 1/7] bpf: Guard __get_user acesss with access_ok for uprobe_multi data Jiri Olsa
` (6 more replies)
0 siblings, 7 replies; 12+ messages in thread
From: Jiri Olsa @ 2026-06-11 11:42 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
Oleg Nesterov, Masami Hiramatsu
hi,
we'd like to be able to open uprobe_multi link on top of binary identified
by file descriptor. This allows us to avoid the race where the binary is
replaced between path resolution and attachment, ensuring we monitor the
intended binary.
v1: https://lore.kernel.org/bpf/20260609104244.588321-1-jolsa@kernel.org/T/#m0275d5f39805c57dc8fd3308c640237dc7aec4db
v2: https://lore.kernel.org/bpf/20260610143627.804790-1-jolsa@kernel.org/T/#m153e18fa426140fdcc773cc97b10e006531656c0
v3 changes:
- guard t_user acesss with access_ok [sashiko]
v2 changes:
- move path retrieval in separate function so CLASS(..) is not used in function
with goto-based cleanup [sashiko]
- force zero path_fd in case BPF_F_UPROBE_MULTI_PATH_FD is not set [sashiko]
- add space around | in bpf_uprobe_multi_link_attach [Alexei]
thanks,
jirka
---
Jiri Olsa (7):
bpf: Guard __get_user acesss with access_ok for uprobe_multi data
bpf: Use user_path_at for path resolution in uprobe_multi
bpf: Add support to specify uprobe_multi target via file descriptor
libbpf: Add path_fd to struct bpf_link_create_opts
selftests/bpf: Add uprobe_multi path_fd test
selftests/bpf: Add uprobe_multi path_fd fail tests
selftests/bpf: Fix typo in verify_umulti_link_info
include/uapi/linux/bpf.h | 7 +++++-
kernel/bpf/syscall.c | 4 +--
kernel/trace/bpf_trace.c | 60 +++++++++++++++++++++++++++++++++++----------
tools/include/uapi/linux/bpf.h | 7 +++++-
tools/lib/bpf/bpf.c | 1 +
tools/lib/bpf/bpf.h | 3 ++-
tools/testing/selftests/bpf/prog_tests/fill_link_info.c | 2 +-
tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
8 files changed, 158 insertions(+), 20 deletions(-)
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCHv3 bpf-next 1/7] bpf: Guard __get_user acesss with access_ok for uprobe_multi data
2026-06-11 11:42 [PATCHv3 bpf-next 0/7] bpf: Allow uprobe_multi binary specified by file descriptor Jiri Olsa
@ 2026-06-11 11:42 ` Jiri Olsa
2026-06-11 11:55 ` sashiko-bot
2026-06-11 11:42 ` [PATCHv3 bpf-next 2/7] bpf: Use user_path_at for path resolution in uprobe_multi Jiri Olsa
` (5 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Jiri Olsa @ 2026-06-11 11:42 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
Oleg Nesterov, Masami Hiramatsu
As reported by sashiko [1] we need to use access_ok to check the user
space data bounds before we use __get-user to get it.
[1] https://lore.kernel.org/bpf/20260610145235.CB1441F00893@smtp.kernel.org/
Fixes: 0b779b61f651 ("bpf: Add cookies support for uprobe_multi link")
Fixes: 89ae89f53d20 ("bpf: Add multi uprobe link")
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
kernel/trace/bpf_trace.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 90432f0fc2a8..b5a12af2d3f8 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -3224,6 +3224,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
unsigned long __user *uoffsets;
u64 __user *ucookies;
void __user *upath;
+ unsigned long size;
u32 flags, cnt, i;
struct path path;
char *name;
@@ -3261,6 +3262,16 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
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);
+ /*
+ * All uoffsets/uref_ctr_offsets/ucookies arrays have the same value
+ * size, we need to check their address range is safe for __get_user
+ * calls.
+ */
+ size = sizeof(*uoffsets) * cnt;
+ if (!access_ok(uoffsets, size) || !access_ok(uref_ctr_offsets, size) ||
+ !access_ok(ucookies, size))
+ return -EFAULT;
+
name = strndup_user(upath, PATH_MAX);
if (IS_ERR(name)) {
err = PTR_ERR(name);
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCHv3 bpf-next 2/7] bpf: Use user_path_at for path resolution in uprobe_multi
2026-06-11 11:42 [PATCHv3 bpf-next 0/7] bpf: Allow uprobe_multi binary specified by file descriptor Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 1/7] bpf: Guard __get_user acesss with access_ok for uprobe_multi data Jiri Olsa
@ 2026-06-11 11:42 ` Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 3/7] bpf: Add support to specify uprobe_multi target via file descriptor Jiri Olsa
` (4 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Jiri Olsa @ 2026-06-11 11:42 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
Oleg Nesterov, Masami Hiramatsu
Resolve the uprobe_multi user path with user_path_at() instead of copying
the string with strndup_user() and passing it to kern_path(). This removes
the temporary allocation and keeps the lookup logic in one helper.
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
kernel/trace/bpf_trace.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index b5a12af2d3f8..f8990bc6b64c 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -3227,7 +3227,6 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
unsigned long size;
u32 flags, cnt, i;
struct path path;
- char *name;
pid_t pid;
int err;
@@ -3272,14 +3271,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
!access_ok(ucookies, size))
return -EFAULT;
- 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 = user_path_at(AT_FDCWD, upath, LOOKUP_FOLLOW, &path);
if (err)
return err;
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCHv3 bpf-next 3/7] bpf: Add support to specify uprobe_multi target via file descriptor
2026-06-11 11:42 [PATCHv3 bpf-next 0/7] bpf: Allow uprobe_multi binary specified by file descriptor Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 1/7] bpf: Guard __get_user acesss with access_ok for uprobe_multi data Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 2/7] bpf: Use user_path_at for path resolution in uprobe_multi Jiri Olsa
@ 2026-06-11 11:42 ` Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 4/7] libbpf: Add path_fd to struct bpf_link_create_opts Jiri Olsa
` (3 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Jiri Olsa @ 2026-06-11 11:42 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
Oleg Nesterov, Masami Hiramatsu
Allow uprobe_multi link to identify the target binary by an already
opened file descriptor.
Adding new BPF_F_UPROBE_MULTI_PATH_FD flag and the path_fd field for
the attr.link_create.uprobe_multi struct.
When the flag is set, we resolve the target from path_fd, without the
flag, we keep the existing string path behavior.
I don't see a use case for supporting O_PATH file descriptors, because
we need to read the binary first to get probes offsets, so I'm using
the CLASS(fd, f), which fails for O_PATH fds.
Assisted-by: Codex:GPT-5.4
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
include/uapi/linux/bpf.h | 7 +++++-
kernel/bpf/syscall.c | 4 ++--
kernel/trace/bpf_trace.c | 43 +++++++++++++++++++++++++++++-----
tools/include/uapi/linux/bpf.h | 7 +++++-
4 files changed, 51 insertions(+), 10 deletions(-)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 11dd610fa5fa..89b36de5fdbb 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -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
@@ -1864,6 +1868,7 @@ union bpf_attr {
__u32 cnt;
__u32 flags;
__u32 pid;
+ __u32 path_fd;
} uprobe_multi;
struct {
union {
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 7ed949f70f82..b44106c8ea75 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -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]);
@@ -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;
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index f8990bc6b64c..82f8feea6931 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -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>
@@ -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;
@@ -3223,7 +3256,6 @@ 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;
unsigned long size;
u32 flags, cnt, i;
struct path path;
@@ -3241,19 +3273,18 @@ 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;
@@ -3271,7 +3302,7 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
!access_ok(ucookies, size))
return -EFAULT;
- err = user_path_at(AT_FDCWD, upath, LOOKUP_FOLLOW, &path);
+ err = bpf_uprobe_multi_get_path(attr, &path);
if (err)
return err;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 11dd610fa5fa..89b36de5fdbb 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -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
@@ -1864,6 +1868,7 @@ union bpf_attr {
__u32 cnt;
__u32 flags;
__u32 pid;
+ __u32 path_fd;
} uprobe_multi;
struct {
union {
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCHv3 bpf-next 4/7] libbpf: Add path_fd to struct bpf_link_create_opts
2026-06-11 11:42 [PATCHv3 bpf-next 0/7] bpf: Allow uprobe_multi binary specified by file descriptor Jiri Olsa
` (2 preceding siblings ...)
2026-06-11 11:42 ` [PATCHv3 bpf-next 3/7] bpf: Add support to specify uprobe_multi target via file descriptor Jiri Olsa
@ 2026-06-11 11:42 ` Jiri Olsa
2026-06-11 11:53 ` sashiko-bot
2026-06-11 11:42 ` [PATCHv3 bpf-next 5/7] selftests/bpf: Add uprobe_multi path_fd test Jiri Olsa
` (2 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Jiri Olsa @ 2026-06-11 11:42 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
Oleg Nesterov, Masami Hiramatsu
Adding the path_fd field to struct bpf_link_create_opts and passing it
through kernel attr interface.
Assisted-by: Codex:GPT-5.4
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
tools/lib/bpf/bpf.c | 1 +
tools/lib/bpf/bpf.h | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index f37e3416f61a..96819c082c77 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -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;
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 012354131cf6..7534a593edae 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -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;
@@ -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,
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCHv3 bpf-next 5/7] selftests/bpf: Add uprobe_multi path_fd test
2026-06-11 11:42 [PATCHv3 bpf-next 0/7] bpf: Allow uprobe_multi binary specified by file descriptor Jiri Olsa
` (3 preceding siblings ...)
2026-06-11 11:42 ` [PATCHv3 bpf-next 4/7] libbpf: Add path_fd to struct bpf_link_create_opts Jiri Olsa
@ 2026-06-11 11:42 ` Jiri Olsa
2026-06-11 11:52 ` sashiko-bot
2026-06-11 11:42 ` [PATCHv3 bpf-next 6/7] selftests/bpf: Add uprobe_multi path_fd fail tests Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 7/7] selftests/bpf: Fix typo in verify_umulti_link_info Jiri Olsa
6 siblings, 1 reply; 12+ messages in thread
From: Jiri Olsa @ 2026-06-11 11:42 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
Oleg Nesterov, Masami Hiramatsu
Add a uprobe_multi link API selftest that opens /proc/self/exe and passes
the resulting descriptor through opts.uprobe_multi.path_fd with
BPF_F_UPROBE_MULTI_PATH_FD set.
Assisted-by: Codex:GPT-5.4
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
.../bpf/prog_tests/uprobe_multi_test.c | 62 +++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
index 56cbea280fbd..ffcf3c92f047 100644
--- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
@@ -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"
@@ -757,6 +758,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)
{
@@ -1354,6 +1414,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"))
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCHv3 bpf-next 6/7] selftests/bpf: Add uprobe_multi path_fd fail tests
2026-06-11 11:42 [PATCHv3 bpf-next 0/7] bpf: Allow uprobe_multi binary specified by file descriptor Jiri Olsa
` (4 preceding siblings ...)
2026-06-11 11:42 ` [PATCHv3 bpf-next 5/7] selftests/bpf: Add uprobe_multi path_fd test Jiri Olsa
@ 2026-06-11 11:42 ` Jiri Olsa
2026-06-11 11:54 ` sashiko-bot
2026-06-11 11:42 ` [PATCHv3 bpf-next 7/7] selftests/bpf: Fix typo in verify_umulti_link_info Jiri Olsa
6 siblings, 1 reply; 12+ messages in thread
From: Jiri Olsa @ 2026-06-11 11:42 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
Oleg Nesterov, Masami Hiramatsu
Adding tests to attach_api_fails suite to make sure we fail
wrong setup for path_fd usage.
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
.../bpf/prog_tests/uprobe_multi_test.c | 32 ++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
index ffcf3c92f047..f0baf5738b75 100644
--- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
@@ -537,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)
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCHv3 bpf-next 7/7] selftests/bpf: Fix typo in verify_umulti_link_info
2026-06-11 11:42 [PATCHv3 bpf-next 0/7] bpf: Allow uprobe_multi binary specified by file descriptor Jiri Olsa
` (5 preceding siblings ...)
2026-06-11 11:42 ` [PATCHv3 bpf-next 6/7] selftests/bpf: Add uprobe_multi path_fd fail tests Jiri Olsa
@ 2026-06-11 11:42 ` Jiri Olsa
6 siblings, 0 replies; 12+ messages in thread
From: Jiri Olsa @ 2026-06-11 11:42 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
Cc: bpf, Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
Oleg Nesterov, Masami Hiramatsu
We verify info.uprobe_multi.flags against wrong kprobe-multi flag
(BPF_F_KPROBE_MULTI_RETURN). It's the same value as the correct
flag (BPF_F_UPROBE_MULTI_RETURN), so there's not functional change.
Fixes: 147c69307bcf ("selftests/bpf: Add link_info test for uprobe_multi link")
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
tools/testing/selftests/bpf/prog_tests/fill_link_info.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c
index e40114620751..f589eefbf9fb 100644
--- a/tools/testing/selftests/bpf/prog_tests/fill_link_info.c
+++ b/tools/testing/selftests/bpf/prog_tests/fill_link_info.c
@@ -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");
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCHv3 bpf-next 5/7] selftests/bpf: Add uprobe_multi path_fd test
2026-06-11 11:42 ` [PATCHv3 bpf-next 5/7] selftests/bpf: Add uprobe_multi path_fd test Jiri Olsa
@ 2026-06-11 11:52 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-11 11:52 UTC (permalink / raw)
To: Jiri Olsa; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The selftest uses a non-existent structure member `opts.uprobe_multi.path_fd` and an undefined macro `BPF_F_UPROBE_MULTI_PATH_FD`, which will cause a build failure.
--
commit 7f1afda8cdc452975b891e8563b1e8e01d191c3a
Author: Jiri Olsa <jolsa@kernel.org>
selftests/bpf: Add uprobe_multi path_fd test
This patch adds a uprobe_multi link API selftest. The test opens
/proc/self/exe and passes the descriptor through opts.uprobe_multi.path_fd
using the BPF_F_UPROBE_MULTI_PATH_FD flag.
> diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> index 56cbea280fbd2..ffcf3c92f0474 100644
> --- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
[ ... ]
> @@ -757,6 +758,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;
[ ... ]
> + 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;
[Severity: Low]
Does the uprobe_multi structure in bpf_link_create_opts actually have a
path_fd member? It appears this code references an undefined struct member
along with an undefined macro BPF_F_UPROBE_MULTI_PATH_FD. Will this cause
a build failure?
> +
> + skel = uprobe_multi__open_and_load();
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611114230.950379-6-jolsa@kernel.org?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv3 bpf-next 4/7] libbpf: Add path_fd to struct bpf_link_create_opts
2026-06-11 11:42 ` [PATCHv3 bpf-next 4/7] libbpf: Add path_fd to struct bpf_link_create_opts Jiri Olsa
@ 2026-06-11 11:53 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-11 11:53 UTC (permalink / raw)
To: Jiri Olsa; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] The kernel-side implementation for processing `path_fd` is completely missing.
- [Medium] The high-level libbpf API for uprobe_multi was not updated to support `path_fd`.
- [Low] The patch references a non-existent `path_fd` field in `union bpf_attr`'s `uprobe_multi` struct, which will cause a fatal compilation failure.
- [Low] The `path_fd` field is incorrectly typed as `__u32` instead of a signed integer, breaking proper semantics for special directory file descriptors like `AT_FDCWD`.
--
commit f8c10c02ad07ee86163cb02487766ca5ac58a262
Author: Jiri Olsa <jolsa@kernel.org>
libbpf: Add path_fd to struct bpf_link_create_opts
This patch adds the path_fd field to struct bpf_link_create_opts and
attempts to pass it through the kernel attribute interface for uprobe_multi
links.
> libbpf: Add path_fd to struct bpf_link_create_opts
>
> Adding the path_fd field to struct bpf_link_create_opts and passing it
> through kernel attr interface.
[Severity: Medium]
Are there missing kernel-side changes in this patch? The commit message
states the field is being passed through the kernel attr interface, but there
are no corresponding changes in sys_bpf() or bpf_uprobe_multi_link_attach()
to extract and utilize the path_fd parameter.
[Severity: Medium]
Will this feature be exposed to the high-level libbpf API? The path_fd field
was added to the low-level struct bpf_link_create_opts, but the user-facing
struct bpf_uprobe_multi_opts and bpf_program__attach_uprobe_multi() do not
appear to be updated to support it.
> diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
> index f37e3416f61a5..96819c082c772 100644
> --- a/tools/lib/bpf/bpf.c
> +++ b/tools/lib/bpf/bpf.c
> @@ -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);
[Severity: Low]
Does this assignment cause a build failure? It looks like the path_fd field
has not been added to the UAPI union bpf_attr for uprobe_multi.
> if (!OPTS_ZEROED(opts, uprobe_multi))
> return libbpf_err(-EINVAL);
> break;
[ ... ]
> diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
> index 012354131cf66..7534a593edae2 100644
> --- a/tools/lib/bpf/bpf.h
> +++ b/tools/lib/bpf/bpf.h
> @@ -444,6 +444,7 @@ struct bpf_link_create_opts {
> const unsigned long *ref_ctr_offsets;
> const __u64 *cookies;
> __u32 pid;
> + __u32 path_fd;
[Severity: Low]
Should path_fd be defined as a signed integer, such as __s32 or int? File
descriptors intended for path lookup need to support special values like
AT_FDCWD (-100), which will wrap around if stored in an unsigned 32-bit
variable.
> } uprobe_multi;
> struct {
> __u64 cookie;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611114230.950379-5-jolsa@kernel.org?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv3 bpf-next 6/7] selftests/bpf: Add uprobe_multi path_fd fail tests
2026-06-11 11:42 ` [PATCHv3 bpf-next 6/7] selftests/bpf: Add uprobe_multi path_fd fail tests Jiri Olsa
@ 2026-06-11 11:54 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-11 11:54 UTC (permalink / raw)
To: Jiri Olsa; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The patch introduces a compilation failure because it uses the undeclared macro BPF_F_UPROBE_MULTI_PATH_FD and references a non-existent struct member 'path_fd' in struct bpf_link_create_opts.
--
commit acbff7cfc9a00c3bfed7027c436c6abc5a678d5d
Author: Jiri Olsa <jolsa@kernel.org>
selftests/bpf: Add uprobe_multi path_fd fail tests
This commit adds new tests to the attach_api_fails suite in
uprobe_multi_test.c to ensure that incorrect setups using path_fd fail
as expected.
> diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> index 56cbea280fbd2..58a7db7fba902 100644
> --- a/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
> +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_multi_test.c
[ ... ]
> @@ -536,7 +536,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,
[Severity: Low]
Does this cause a compilation failure? The struct member path_fd in
bpf_link_create_opts and the BPF_F_UPROBE_MULTI_PATH_FD macro appear to be
missing or undeclared here, which would typically cause the compiler to fail
with an undeclared identifier error.
> + .uprobe_multi.offsets = (unsigned long *)&offset,
> + .uprobe_multi.cnt = 1,
> + );
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611114230.950379-7-jolsa@kernel.org?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCHv3 bpf-next 1/7] bpf: Guard __get_user acesss with access_ok for uprobe_multi data
2026-06-11 11:42 ` [PATCHv3 bpf-next 1/7] bpf: Guard __get_user acesss with access_ok for uprobe_multi data Jiri Olsa
@ 2026-06-11 11:55 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-11 11:55 UTC (permalink / raw)
To: Jiri Olsa; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing access_ok() check for the usyms array in bpf_kprobe_multi_link_attach() allows arbitrary kernel memory read.
--
commit e5440667240e003d50bb838ad4b19e526ef6c703
Author: Jiri Olsa <jolsa@kernel.org>
bpf: Guard __get_user acesss with access_ok for uprobe_multi data
This patch adds access_ok checks for uprobe_multi arrays before calling
__get_user. It ensures that the user space data bounds are safe.
Link: https://lore.kernel.org/bpf/20260610145235.CB1441F00893@smtp.kernel.org/
> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
[ ... ]
> @@ -3261,6 +3262,16 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
> 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);
>
> + /*
> + * All uoffsets/uref_ctr_offsets/ucookies arrays have the same value
> + * size, we need to check their address range is safe for __get_user
> + * calls.
> + */
> + size = sizeof(*uoffsets) * cnt;
> + if (!access_ok(uoffsets, size) || !access_ok(uref_ctr_offsets, size) ||
> + !access_ok(ucookies, size))
> + return -EFAULT;
[Severity: High]
This is a pre-existing issue, but while you are adding these access checks
for uprobes, does bpf_kprobe_multi_link_attach() have an identical missing
check for the usyms array?
Looking at bpf_kprobe_multi_link_attach(), it calls copy_user_syms() with
the unchecked user pointer usyms:
kernel/trace/bpf_trace.c:bpf_kprobe_multi_link_attach() {
...
struct user_syms us;
err = copy_user_syms(&us, usyms, cnt);
if (err)
goto error;
...
}
And copy_user_syms() iteratively calls __get_user(usymbol, usyms + i)
without any prior access checks on the array:
kernel/trace/bpf_trace.c:copy_user_syms() {
...
for (p = buf, i = 0; i < cnt; i++) {
if (__get_user(usymbol, usyms + i)) {
err = -EFAULT;
goto error;
}
...
}
Since __get_user() bypasses bounds checking, couldn't an attacker provide
a kernel address for usyms and read arbitrary kernel memory into usymbol?
A subsequent strncpy_from_user() call would use usymbol as its source
pointer, potentially acting as a side-channel oracle to leak kernel
information or bypass kernel address layout randomization.
Should copy_user_syms() or bpf_kprobe_multi_link_attach() also include
an access_ok() check for the usyms array?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260611114230.950379-2-jolsa@kernel.org?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-06-11 11:55 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-11 11:42 [PATCHv3 bpf-next 0/7] bpf: Allow uprobe_multi binary specified by file descriptor Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 1/7] bpf: Guard __get_user acesss with access_ok for uprobe_multi data Jiri Olsa
2026-06-11 11:55 ` sashiko-bot
2026-06-11 11:42 ` [PATCHv3 bpf-next 2/7] bpf: Use user_path_at for path resolution in uprobe_multi Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 3/7] bpf: Add support to specify uprobe_multi target via file descriptor Jiri Olsa
2026-06-11 11:42 ` [PATCHv3 bpf-next 4/7] libbpf: Add path_fd to struct bpf_link_create_opts Jiri Olsa
2026-06-11 11:53 ` sashiko-bot
2026-06-11 11:42 ` [PATCHv3 bpf-next 5/7] selftests/bpf: Add uprobe_multi path_fd test Jiri Olsa
2026-06-11 11:52 ` sashiko-bot
2026-06-11 11:42 ` [PATCHv3 bpf-next 6/7] selftests/bpf: Add uprobe_multi path_fd fail tests Jiri Olsa
2026-06-11 11:54 ` sashiko-bot
2026-06-11 11:42 ` [PATCHv3 bpf-next 7/7] selftests/bpf: Fix typo in verify_umulti_link_info Jiri Olsa
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox