public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH bpf-next 0/1] Upgrading uprobe and kprobe to their `multi` counterparts.
@ 2026-02-12 15:20 Varun R Mallya
  2026-02-12 15:20 ` [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported Varun R Mallya
  2026-02-18 19:07 ` [RFC PATCH bpf-next 0/1] Upgrading uprobe and kprobe to their `multi` counterparts Andrii Nakryiko
  0 siblings, 2 replies; 9+ messages in thread
From: Varun R Mallya @ 2026-02-12 15:20 UTC (permalink / raw)
  To: andrii, alan.maguire; +Cc: ast, daniel, bpf, linux-kernel, varunrmallya

This RFC patch explores auto-upgrading standard uprobes to use the 
multi-uprobe infrastructure when supported by the underlying kernel.

Background:
The BPF token concept allows privileged operations inside non-privileged 
user namespaces. However, attaching standard uprobes and kprobes 
currently relies on the perf_event_open() syscall, which is not BPF 
token-aware. Multi-uprobes and multi-kprobes bypass 
perf_event_open() entirely, attaching via the bpf() syscall instead, 
making them compatible with BPF tokens. 

To bridge this gap, the goal is to switch SEC("uprobe") and 
SEC("kprobe") to use multi-uprobe/kprobe under the hood. To maintain 
backward compatibility for cases where singular uprobes are explicitly 
desired, this patch also introduces SEC("uprobe.single") and 
SEC("kprobe.single").

Current Implementation:
The decision to upgrade is made at BPF program load time in 
`bpf_object_init_progs()`. If the kernel supports FEAT_UPROBE_MULTI_LINK,
we intercept programs with section names matching "u[ret]probe" and change 
their `expected_attach_type` to BPF_TRACE_UPROBE_MULTI.

During attachment, `attach_uprobe` checks this expected type and 
routes the attachment to `bpf_program__attach_uprobe_multi` accordingly.

I am currently hitting an issue with selftests, and I would appreciate 
some guidance before proceeding to implement this for kprobes and fix it
for uprobes.

Selftests that rely on auto-attachment pass. However, tests that bypass 
auto-attach and manually call `bpf_program__attach_uprobe_opts()` are 
failing. Because the program's `expected_attach_type` is modified to 
multi-uprobe at load time inside `bpf_object_init_progs()`, directly 
calling the legacy attach options subsequently dies due to the type 
mismatch. 

How should we handle legacy manual attachments for auto-upgraded programs? 
Should `bpf_program__attach_uprobe_opts()` be taught to internally 
redirect to the multi-uprobe path if the expected type was upgraded?

Thanks,
Varun

Varun R Mallya (1):
  libbpf: Auto-upgrade uprobes to multi-uprobes when supported

 tools/lib/bpf/libbpf.c | 42 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 36 insertions(+), 6 deletions(-)

-- 
2.52.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported
  2026-02-12 15:20 [RFC PATCH bpf-next 0/1] Upgrading uprobe and kprobe to their `multi` counterparts Varun R Mallya
@ 2026-02-12 15:20 ` Varun R Mallya
  2026-02-13  0:06   ` Yonghong Song
  2026-02-18 19:03   ` Andrii Nakryiko
  2026-02-18 19:07 ` [RFC PATCH bpf-next 0/1] Upgrading uprobe and kprobe to their `multi` counterparts Andrii Nakryiko
  1 sibling, 2 replies; 9+ messages in thread
From: Varun R Mallya @ 2026-02-12 15:20 UTC (permalink / raw)
  To: andrii, alan.maguire; +Cc: ast, daniel, bpf, linux-kernel, varunrmallya

This patch modifies libbpf to automatically "upgrade" standard
SEC("uprobe") and SEC("uretprobe") programs to use the multi-uprobe
infrastructure (BPF_TRACE_UPROBE_MULTI) at load time if the kernel
supports it, making them compatible with BPF tokens.

To maintain backward compatibility and handle rare cases where singular
uprobes are required, new SEC("uprobe.single") and SEC("uretprobe.single")
section types are introduced. These force libbpf to use the legacy
perf_event_open() attachment path.

Signed-off-by: Varun R Mallya <varunrmallya@gmail.com>
---
 tools/lib/bpf/libbpf.c | 42 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 36 insertions(+), 6 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 0c8bf0b5cce4..a32f221d3245 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8265,6 +8265,22 @@ static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_object
 		prog->type = prog->sec_def->prog_type;
 		prog->expected_attach_type = prog->sec_def->expected_attach_type;
 
+		/* set BPF_TRACE_UPROBE_MULTI if sec_name matches "u[ret]probe"
+		 * otherwise, leave alone.
+		 */
+		if (kernel_supports(obj, FEAT_UPROBE_MULTI_LINK)) {
+			char *probe_type = NULL;
+			int n;
+
+			n = sscanf(prog->sec_name, "%m[^/]", &probe_type);
+			if (n >= 1)
+				if (!strcmp(probe_type, "uprobe") ||
+				    !strcmp(probe_type, "uretprobe"))
+					prog->expected_attach_type = BPF_TRACE_UPROBE_MULTI;
+
+			free(probe_type);
+		}
+
 		/* sec_def can have custom callback which should be called
 		 * after bpf_program is initialized to adjust its properties
 		 */
@@ -9822,9 +9838,11 @@ static const struct bpf_sec_def section_defs[] = {
 	SEC_DEF("kprobe+",		KPROBE,	0, SEC_NONE, attach_kprobe),
 	SEC_DEF("uprobe+",		KPROBE,	0, SEC_NONE, attach_uprobe),
 	SEC_DEF("uprobe.s+",		KPROBE,	0, SEC_SLEEPABLE, attach_uprobe),
+	SEC_DEF("uprobe.single+",	KPROBE,	0, SEC_NONE, attach_uprobe),
 	SEC_DEF("kretprobe+",		KPROBE, 0, SEC_NONE, attach_kprobe),
 	SEC_DEF("uretprobe+",		KPROBE, 0, SEC_NONE, attach_uprobe),
 	SEC_DEF("uretprobe.s+",		KPROBE, 0, SEC_SLEEPABLE, attach_uprobe),
+	SEC_DEF("uretprobe.single+",	KPROBE,	0, SEC_NONE, attach_uprobe),
 	SEC_DEF("kprobe.multi+",	KPROBE,	BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
 	SEC_DEF("kretprobe.multi+",	KPROBE,	BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
 	SEC_DEF("kprobe.session+",	KPROBE,	BPF_TRACE_KPROBE_SESSION, SEC_NONE, attach_kprobe_session),
@@ -12722,10 +12740,10 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
  */
 static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link)
 {
-	DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
 	char *probe_type = NULL, *binary_path = NULL, *func_name = NULL, *func_off;
 	int n, c, ret = -EINVAL;
 	long offset = 0;
+	bool is_retprobe;
 
 	*link = NULL;
 
@@ -12752,15 +12770,27 @@ static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf
 			else
 				offset = 0;
 		}
-		opts.retprobe = strcmp(probe_type, "uretprobe") == 0 ||
-				strcmp(probe_type, "uretprobe.s") == 0;
-		if (opts.retprobe && offset != 0) {
+		is_retprobe = strcmp(probe_type, "uretprobe") == 0 ||
+			      strcmp(probe_type, "uretprobe.s") == 0;
+		if (is_retprobe && offset != 0) {
 			pr_warn("prog '%s': uretprobes do not support offset specification\n",
 				prog->name);
 			break;
 		}
-		opts.func_name = func_name;
-		*link = bpf_program__attach_uprobe_opts(prog, -1, binary_path, offset, &opts);
+		if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI) {
+			DECLARE_LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
+
+			opts.retprobe = is_retprobe;
+			*link = bpf_program__attach_uprobe_multi(prog, -1, binary_path,
+								 func_name, &opts);
+		} else {
+			DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
+
+			opts.retprobe = is_retprobe;
+			opts.func_name = func_name;
+			*link = bpf_program__attach_uprobe_opts(prog, -1, binary_path,
+								offset, &opts);
+		}
 		ret = libbpf_get_error(*link);
 		break;
 	default:
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported
  2026-02-12 15:20 ` [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported Varun R Mallya
@ 2026-02-13  0:06   ` Yonghong Song
  2026-02-13 17:22     ` Varun R Mallya
  2026-02-18 19:03   ` Andrii Nakryiko
  1 sibling, 1 reply; 9+ messages in thread
From: Yonghong Song @ 2026-02-13  0:06 UTC (permalink / raw)
  To: Varun R Mallya, andrii, alan.maguire; +Cc: ast, daniel, bpf, linux-kernel



On 2/12/26 7:20 AM, Varun R Mallya wrote:
> This patch modifies libbpf to automatically "upgrade" standard
> SEC("uprobe") and SEC("uretprobe") programs to use the multi-uprobe
> infrastructure (BPF_TRACE_UPROBE_MULTI) at load time if the kernel
> supports it, making them compatible with BPF tokens.
>
> To maintain backward compatibility and handle rare cases where singular
> uprobes are required, new SEC("uprobe.single") and SEC("uretprobe.single")
> section types are introduced. These force libbpf to use the legacy
> perf_event_open() attachment path.

Maybe you can have bpf programs for both uprobe/uretprobe
and uprobe.multi/uretprobe.multi?

You can add "?" before the section name (e.g., SEC("?uprobe") so you can
selectively enable those programs before loading. This one if one choice
e.g. uprobe/uretprobe is not working, you can then try
uprobe.multi/uretprobe.multi.

>
> Signed-off-by: Varun R Mallya <varunrmallya@gmail.com>
> ---
>   tools/lib/bpf/libbpf.c | 42 ++++++++++++++++++++++++++++++++++++------
>   1 file changed, 36 insertions(+), 6 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 0c8bf0b5cce4..a32f221d3245 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -8265,6 +8265,22 @@ static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_object
>   		prog->type = prog->sec_def->prog_type;
>   		prog->expected_attach_type = prog->sec_def->expected_attach_type;
>   
> +		/* set BPF_TRACE_UPROBE_MULTI if sec_name matches "u[ret]probe"
> +		 * otherwise, leave alone.
> +		 */
> +		if (kernel_supports(obj, FEAT_UPROBE_MULTI_LINK)) {
> +			char *probe_type = NULL;
> +			int n;
> +
> +			n = sscanf(prog->sec_name, "%m[^/]", &probe_type);
> +			if (n >= 1)
> +				if (!strcmp(probe_type, "uprobe") ||
> +				    !strcmp(probe_type, "uretprobe"))
> +					prog->expected_attach_type = BPF_TRACE_UPROBE_MULTI;
> +
> +			free(probe_type);
> +		}
> +
>   		/* sec_def can have custom callback which should be called
>   		 * after bpf_program is initialized to adjust its properties
>   		 */
> @@ -9822,9 +9838,11 @@ static const struct bpf_sec_def section_defs[] = {
>   	SEC_DEF("kprobe+",		KPROBE,	0, SEC_NONE, attach_kprobe),
>   	SEC_DEF("uprobe+",		KPROBE,	0, SEC_NONE, attach_uprobe),
>   	SEC_DEF("uprobe.s+",		KPROBE,	0, SEC_SLEEPABLE, attach_uprobe),
> +	SEC_DEF("uprobe.single+",	KPROBE,	0, SEC_NONE, attach_uprobe),
>   	SEC_DEF("kretprobe+",		KPROBE, 0, SEC_NONE, attach_kprobe),
>   	SEC_DEF("uretprobe+",		KPROBE, 0, SEC_NONE, attach_uprobe),
>   	SEC_DEF("uretprobe.s+",		KPROBE, 0, SEC_SLEEPABLE, attach_uprobe),
> +	SEC_DEF("uretprobe.single+",	KPROBE,	0, SEC_NONE, attach_uprobe),
>   	SEC_DEF("kprobe.multi+",	KPROBE,	BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
>   	SEC_DEF("kretprobe.multi+",	KPROBE,	BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
>   	SEC_DEF("kprobe.session+",	KPROBE,	BPF_TRACE_KPROBE_SESSION, SEC_NONE, attach_kprobe_session),
> @@ -12722,10 +12740,10 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
>    */
>   static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link)
>   {
> -	DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
>   	char *probe_type = NULL, *binary_path = NULL, *func_name = NULL, *func_off;
>   	int n, c, ret = -EINVAL;
>   	long offset = 0;
> +	bool is_retprobe;
>   
>   	*link = NULL;
>   
> @@ -12752,15 +12770,27 @@ static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf
>   			else
>   				offset = 0;
>   		}
> -		opts.retprobe = strcmp(probe_type, "uretprobe") == 0 ||
> -				strcmp(probe_type, "uretprobe.s") == 0;
> -		if (opts.retprobe && offset != 0) {
> +		is_retprobe = strcmp(probe_type, "uretprobe") == 0 ||
> +			      strcmp(probe_type, "uretprobe.s") == 0;
> +		if (is_retprobe && offset != 0) {
>   			pr_warn("prog '%s': uretprobes do not support offset specification\n",
>   				prog->name);
>   			break;
>   		}
> -		opts.func_name = func_name;
> -		*link = bpf_program__attach_uprobe_opts(prog, -1, binary_path, offset, &opts);
> +		if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI) {
> +			DECLARE_LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);
> +
> +			opts.retprobe = is_retprobe;
> +			*link = bpf_program__attach_uprobe_multi(prog, -1, binary_path,
> +								 func_name, &opts);
> +		} else {
> +			DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
> +
> +			opts.retprobe = is_retprobe;
> +			opts.func_name = func_name;
> +			*link = bpf_program__attach_uprobe_opts(prog, -1, binary_path,
> +								offset, &opts);
> +		}
>   		ret = libbpf_get_error(*link);
>   		break;
>   	default:


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported
  2026-02-13  0:06   ` Yonghong Song
@ 2026-02-13 17:22     ` Varun R Mallya
  2026-02-14  5:25       ` Yonghong Song
  0 siblings, 1 reply; 9+ messages in thread
From: Varun R Mallya @ 2026-02-13 17:22 UTC (permalink / raw)
  To: Yonghong Song; +Cc: andrii, alan.maguire, ast, daniel, bpf, linux-kernel

On Thu, Feb 12, 2026 at 04:06:22PM -0800, Yonghong Song wrote:
>
>
> On 2/12/26 7:20 AM, Varun R Mallya wrote:
> > This patch modifies libbpf to automatically "upgrade" standard
> > SEC("uprobe") and SEC("uretprobe") programs to use the multi-uprobe
> > infrastructure (BPF_TRACE_UPROBE_MULTI) at load time if the kernel
> > supports it, making them compatible with BPF tokens.
> >
> > To maintain backward compatibility and handle rare cases where singular
> > uprobes are required, new SEC("uprobe.single") and SEC("uretprobe.single")
> > section types are introduced. These force libbpf to use the legacy
> > perf_event_open() attachment path.
>
> Maybe you can have bpf programs for both uprobe/uretprobe
> and uprobe.multi/uretprobe.multi?
>
> You can add "?" before the section name (e.g., SEC("?uprobe") so you can
> selectively enable those programs before loading. This one if one choice
> e.g. uprobe/uretprobe is not working, you can then try
> uprobe.multi/uretprobe.multi.

This is a good idea, but isn't making the upgradation built-in a better
choice ?
This way, anyone writing the program does not have to rewrite
the same thing twice, keeping their programs pretty clean. This also
moves the upgradation logic (which is probably going to be repeated multiple times)
into the library which makes it easier for anyone to have something BPF
Token compatible without having to write all this extra logic. Since "uprobe.multi"
is compatible with "uprobe", I don't think anything will break as well.
(The current breakages in the selftests are due to the patch being in
nascent stages and I'll fix it after I get some feedback on my
questions.)


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported
  2026-02-13 17:22     ` Varun R Mallya
@ 2026-02-14  5:25       ` Yonghong Song
  2026-02-18 19:07         ` Andrii Nakryiko
  0 siblings, 1 reply; 9+ messages in thread
From: Yonghong Song @ 2026-02-14  5:25 UTC (permalink / raw)
  To: Varun R Mallya; +Cc: andrii, alan.maguire, ast, daniel, bpf, linux-kernel



On 2/13/26 9:22 AM, Varun R Mallya wrote:
> On Thu, Feb 12, 2026 at 04:06:22PM -0800, Yonghong Song wrote:
>>
>> On 2/12/26 7:20 AM, Varun R Mallya wrote:
>>> This patch modifies libbpf to automatically "upgrade" standard
>>> SEC("uprobe") and SEC("uretprobe") programs to use the multi-uprobe
>>> infrastructure (BPF_TRACE_UPROBE_MULTI) at load time if the kernel
>>> supports it, making them compatible with BPF tokens.
>>>
>>> To maintain backward compatibility and handle rare cases where singular
>>> uprobes are required, new SEC("uprobe.single") and SEC("uretprobe.single")
>>> section types are introduced. These force libbpf to use the legacy
>>> perf_event_open() attachment path.
>> Maybe you can have bpf programs for both uprobe/uretprobe
>> and uprobe.multi/uretprobe.multi?
>>
>> You can add "?" before the section name (e.g., SEC("?uprobe") so you can
>> selectively enable those programs before loading. This one if one choice
>> e.g. uprobe/uretprobe is not working, you can then try
>> uprobe.multi/uretprobe.multi.
> This is a good idea, but isn't making the upgradation built-in a better
> choice ?
> This way, anyone writing the program does not have to rewrite
> the same thing twice, keeping their programs pretty clean. This also
> moves the upgradation logic (which is probably going to be repeated multiple times)
> into the library which makes it easier for anyone to have something BPF
> Token compatible without having to write all this extra logic. Since "uprobe.multi"
> is compatible with "uprobe", I don't think anything will break as well.
> (The current breakages in the selftests are due to the patch being in
> nascent stages and I'll fix it after I get some feedback on my
> questions.)

I still feel this is a hack, esp. for libbpf. The libbpf provides various
APIs as the building block. Automatic upgrading inside libbpf does not
sound right. These upgrading thing should happen in applications.

 From bpf program side, you can have progs for both uprobe and uprobe_multi.
You can have static function which can be used for both uprobe and uprobe_multi.
It should not be hard. Looks at bpf selftest, there are quite some programs
with prefix "?" which gives application a choice whether it should be
enabled or not during to kernel probing or other things.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported
  2026-02-12 15:20 ` [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported Varun R Mallya
  2026-02-13  0:06   ` Yonghong Song
@ 2026-02-18 19:03   ` Andrii Nakryiko
  1 sibling, 0 replies; 9+ messages in thread
From: Andrii Nakryiko @ 2026-02-18 19:03 UTC (permalink / raw)
  To: Varun R Mallya; +Cc: andrii, alan.maguire, ast, daniel, bpf, linux-kernel

On Thu, Feb 12, 2026 at 7:20 AM Varun R Mallya <varunrmallya@gmail.com> wrote:
>
> This patch modifies libbpf to automatically "upgrade" standard
> SEC("uprobe") and SEC("uretprobe") programs to use the multi-uprobe
> infrastructure (BPF_TRACE_UPROBE_MULTI) at load time if the kernel
> supports it, making them compatible with BPF tokens.
>
> To maintain backward compatibility and handle rare cases where singular
> uprobes are required, new SEC("uprobe.single") and SEC("uretprobe.single")
> section types are introduced. These force libbpf to use the legacy
> perf_event_open() attachment path.
>
> Signed-off-by: Varun R Mallya <varunrmallya@gmail.com>
> ---
>  tools/lib/bpf/libbpf.c | 42 ++++++++++++++++++++++++++++++++++++------
>  1 file changed, 36 insertions(+), 6 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 0c8bf0b5cce4..a32f221d3245 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -8265,6 +8265,22 @@ static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_object
>                 prog->type = prog->sec_def->prog_type;
>                 prog->expected_attach_type = prog->sec_def->expected_attach_type;
>
> +               /* set BPF_TRACE_UPROBE_MULTI if sec_name matches "u[ret]probe"
> +                * otherwise, leave alone.
> +                */
> +               if (kernel_supports(obj, FEAT_UPROBE_MULTI_LINK)) {

bpf_object_init_progs() is happening during BPF object open phase,
which is too early to check kernel features. We should postpone
expected_attach_type setting to preparation (bpf_object__prepare)
phase, at which point we expect process to have CAP_BPF and other
permissions.


pw-bot: cr

> +                       char *probe_type = NULL;
> +                       int n;
> +
> +                       n = sscanf(prog->sec_name, "%m[^/]", &probe_type);
> +                       if (n >= 1)
> +                               if (!strcmp(probe_type, "uprobe") ||
> +                                   !strcmp(probe_type, "uretprobe"))
> +                                       prog->expected_attach_type = BPF_TRACE_UPROBE_MULTI;

instead of sscanf+free, can't we just strncmp() the prefix (and make
sure it's either full match, or is followed by forward slash)?

> +
> +                       free(probe_type);
> +               }
> +
>                 /* sec_def can have custom callback which should be called
>                  * after bpf_program is initialized to adjust its properties
>                  */
> @@ -9822,9 +9838,11 @@ static const struct bpf_sec_def section_defs[] = {
>         SEC_DEF("kprobe+",              KPROBE, 0, SEC_NONE, attach_kprobe),
>         SEC_DEF("uprobe+",              KPROBE, 0, SEC_NONE, attach_uprobe),
>         SEC_DEF("uprobe.s+",            KPROBE, 0, SEC_SLEEPABLE, attach_uprobe),
> +       SEC_DEF("uprobe.single+",       KPROBE, 0, SEC_NONE, attach_uprobe),
>         SEC_DEF("kretprobe+",           KPROBE, 0, SEC_NONE, attach_kprobe),
>         SEC_DEF("uretprobe+",           KPROBE, 0, SEC_NONE, attach_uprobe),
>         SEC_DEF("uretprobe.s+",         KPROBE, 0, SEC_SLEEPABLE, attach_uprobe),
> +       SEC_DEF("uretprobe.single+",    KPROBE, 0, SEC_NONE, attach_uprobe),
>         SEC_DEF("kprobe.multi+",        KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
>         SEC_DEF("kretprobe.multi+",     KPROBE, BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
>         SEC_DEF("kprobe.session+",      KPROBE, BPF_TRACE_KPROBE_SESSION, SEC_NONE, attach_kprobe_session),
> @@ -12722,10 +12740,10 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
>   */
>  static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link)
>  {
> -       DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
>         char *probe_type = NULL, *binary_path = NULL, *func_name = NULL, *func_off;
>         int n, c, ret = -EINVAL;
>         long offset = 0;
> +       bool is_retprobe;
>
>         *link = NULL;
>
> @@ -12752,15 +12770,27 @@ static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf
>                         else
>                                 offset = 0;
>                 }
> -               opts.retprobe = strcmp(probe_type, "uretprobe") == 0 ||
> -                               strcmp(probe_type, "uretprobe.s") == 0;
> -               if (opts.retprobe && offset != 0) {
> +               is_retprobe = strcmp(probe_type, "uretprobe") == 0 ||
> +                             strcmp(probe_type, "uretprobe.s") == 0;
> +               if (is_retprobe && offset != 0) {
>                         pr_warn("prog '%s': uretprobes do not support offset specification\n",
>                                 prog->name);
>                         break;
>                 }
> -               opts.func_name = func_name;
> -               *link = bpf_program__attach_uprobe_opts(prog, -1, binary_path, offset, &opts);
> +               if (prog->expected_attach_type == BPF_TRACE_UPROBE_MULTI) {
> +                       DECLARE_LIBBPF_OPTS(bpf_uprobe_multi_opts, opts);

nit: use shorter LIBBPF_OPTS() macro for this, we kept
DECLARE_LIBBPF_OPTS for backwards compat, but there is no reason to
use it going forward

> +
> +                       opts.retprobe = is_retprobe;
> +                       *link = bpf_program__attach_uprobe_multi(prog, -1, binary_path,
> +                                                                func_name, &opts);
> +               } else {
> +                       DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
> +
> +                       opts.retprobe = is_retprobe;
> +                       opts.func_name = func_name;
> +                       *link = bpf_program__attach_uprobe_opts(prog, -1, binary_path,
> +                                                               offset, &opts);
> +               }
>                 ret = libbpf_get_error(*link);
>                 break;
>         default:
> --
> 2.52.0
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported
  2026-02-14  5:25       ` Yonghong Song
@ 2026-02-18 19:07         ` Andrii Nakryiko
  2026-02-19  5:31           ` Yonghong Song
  0 siblings, 1 reply; 9+ messages in thread
From: Andrii Nakryiko @ 2026-02-18 19:07 UTC (permalink / raw)
  To: Yonghong Song
  Cc: Varun R Mallya, andrii, alan.maguire, ast, daniel, bpf,
	linux-kernel

On Fri, Feb 13, 2026 at 9:25 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>
>
>
> On 2/13/26 9:22 AM, Varun R Mallya wrote:
> > On Thu, Feb 12, 2026 at 04:06:22PM -0800, Yonghong Song wrote:
> >>
> >> On 2/12/26 7:20 AM, Varun R Mallya wrote:
> >>> This patch modifies libbpf to automatically "upgrade" standard
> >>> SEC("uprobe") and SEC("uretprobe") programs to use the multi-uprobe
> >>> infrastructure (BPF_TRACE_UPROBE_MULTI) at load time if the kernel
> >>> supports it, making them compatible with BPF tokens.
> >>>
> >>> To maintain backward compatibility and handle rare cases where singular
> >>> uprobes are required, new SEC("uprobe.single") and SEC("uretprobe.single")
> >>> section types are introduced. These force libbpf to use the legacy
> >>> perf_event_open() attachment path.
> >> Maybe you can have bpf programs for both uprobe/uretprobe
> >> and uprobe.multi/uretprobe.multi?
> >>
> >> You can add "?" before the section name (e.g., SEC("?uprobe") so you can
> >> selectively enable those programs before loading. This one if one choice
> >> e.g. uprobe/uretprobe is not working, you can then try
> >> uprobe.multi/uretprobe.multi.
> > This is a good idea, but isn't making the upgradation built-in a better
> > choice ?
> > This way, anyone writing the program does not have to rewrite
> > the same thing twice, keeping their programs pretty clean. This also
> > moves the upgradation logic (which is probably going to be repeated multiple times)
> > into the library which makes it easier for anyone to have something BPF
> > Token compatible without having to write all this extra logic. Since "uprobe.multi"
> > is compatible with "uprobe", I don't think anything will break as well.
> > (The current breakages in the selftests are due to the patch being in
> > nascent stages and I'll fix it after I get some feedback on my
> > questions.)
>
> I still feel this is a hack, esp. for libbpf. The libbpf provides various
> APIs as the building block. Automatic upgrading inside libbpf does not
> sound right. These upgrading thing should happen in applications.
>
>  From bpf program side, you can have progs for both uprobe and uprobe_multi.
> You can have static function which can be used for both uprobe and uprobe_multi.
> It should not be hard. Looks at bpf selftest, there are quite some programs
> with prefix "?" which gives application a choice whether it should be
> enabled or not during to kernel probing or other things.
>

Yeah, you can definitely handle this without needing to duplicate the
logic in BPF code, but the idea here is to make uprobe work
transparently inside user namespaced containers (assuming BPF token
was provided), without having to explicitly accommodate this as a
special mode.

So while it can be seen as a bit of a hack, in practice whether you
use uprobe or uprobe.multi doesn't really matter (they have equivalent
features from BPF/kernel POV), but being able to just use
SEC("uprobe") is great because you don't have to worry about old
kernels not supporting uprobe.multi, plus you get automatic BPF token
compatibility.

This is a bit harder for kprobes because singular kprobe can be
installed at an offset, while kprobe.multi only support offset zero.
But even with kprobe, I think it's worth trying to transparently make
them BPF token-aware using a similar approach.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH bpf-next 0/1] Upgrading uprobe and kprobe to their `multi` counterparts.
  2026-02-12 15:20 [RFC PATCH bpf-next 0/1] Upgrading uprobe and kprobe to their `multi` counterparts Varun R Mallya
  2026-02-12 15:20 ` [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported Varun R Mallya
@ 2026-02-18 19:07 ` Andrii Nakryiko
  1 sibling, 0 replies; 9+ messages in thread
From: Andrii Nakryiko @ 2026-02-18 19:07 UTC (permalink / raw)
  To: Varun R Mallya; +Cc: andrii, alan.maguire, ast, daniel, bpf, linux-kernel

On Thu, Feb 12, 2026 at 7:20 AM Varun R Mallya <varunrmallya@gmail.com> wrote:
>
> This RFC patch explores auto-upgrading standard uprobes to use the
> multi-uprobe infrastructure when supported by the underlying kernel.
>
> Background:
> The BPF token concept allows privileged operations inside non-privileged
> user namespaces. However, attaching standard uprobes and kprobes
> currently relies on the perf_event_open() syscall, which is not BPF
> token-aware. Multi-uprobes and multi-kprobes bypass
> perf_event_open() entirely, attaching via the bpf() syscall instead,
> making them compatible with BPF tokens.
>
> To bridge this gap, the goal is to switch SEC("uprobe") and
> SEC("kprobe") to use multi-uprobe/kprobe under the hood. To maintain
> backward compatibility for cases where singular uprobes are explicitly
> desired, this patch also introduces SEC("uprobe.single") and
> SEC("kprobe.single").
>
> Current Implementation:
> The decision to upgrade is made at BPF program load time in
> `bpf_object_init_progs()`. If the kernel supports FEAT_UPROBE_MULTI_LINK,
> we intercept programs with section names matching "u[ret]probe" and change
> their `expected_attach_type` to BPF_TRACE_UPROBE_MULTI.
>
> During attachment, `attach_uprobe` checks this expected type and
> routes the attachment to `bpf_program__attach_uprobe_multi` accordingly.
>
> I am currently hitting an issue with selftests, and I would appreciate
> some guidance before proceeding to implement this for kprobes and fix it
> for uprobes.
>
> Selftests that rely on auto-attachment pass. However, tests that bypass
> auto-attach and manually call `bpf_program__attach_uprobe_opts()` are
> failing. Because the program's `expected_attach_type` is modified to
> multi-uprobe at load time inside `bpf_object_init_progs()`, directly
> calling the legacy attach options subsequently dies due to the type
> mismatch.
>
> How should we handle legacy manual attachments for auto-upgraded programs?
> Should `bpf_program__attach_uprobe_opts()` be taught to internally
> redirect to the multi-uprobe path if the expected type was upgraded?

I think so. bpf_uprobe_multi_opts() should have all the functionality
necessary, no?


>
> Thanks,
> Varun
>
> Varun R Mallya (1):
>   libbpf: Auto-upgrade uprobes to multi-uprobes when supported
>
>  tools/lib/bpf/libbpf.c | 42 ++++++++++++++++++++++++++++++++++++------
>  1 file changed, 36 insertions(+), 6 deletions(-)
>
> --
> 2.52.0
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported
  2026-02-18 19:07         ` Andrii Nakryiko
@ 2026-02-19  5:31           ` Yonghong Song
  0 siblings, 0 replies; 9+ messages in thread
From: Yonghong Song @ 2026-02-19  5:31 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Varun R Mallya, andrii, alan.maguire, ast, daniel, bpf,
	linux-kernel



On 2/18/26 11:07 AM, Andrii Nakryiko wrote:
> On Fri, Feb 13, 2026 at 9:25 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>>
>>
>> On 2/13/26 9:22 AM, Varun R Mallya wrote:
>>> On Thu, Feb 12, 2026 at 04:06:22PM -0800, Yonghong Song wrote:
>>>> On 2/12/26 7:20 AM, Varun R Mallya wrote:
>>>>> This patch modifies libbpf to automatically "upgrade" standard
>>>>> SEC("uprobe") and SEC("uretprobe") programs to use the multi-uprobe
>>>>> infrastructure (BPF_TRACE_UPROBE_MULTI) at load time if the kernel
>>>>> supports it, making them compatible with BPF tokens.
>>>>>
>>>>> To maintain backward compatibility and handle rare cases where singular
>>>>> uprobes are required, new SEC("uprobe.single") and SEC("uretprobe.single")
>>>>> section types are introduced. These force libbpf to use the legacy
>>>>> perf_event_open() attachment path.
>>>> Maybe you can have bpf programs for both uprobe/uretprobe
>>>> and uprobe.multi/uretprobe.multi?
>>>>
>>>> You can add "?" before the section name (e.g., SEC("?uprobe") so you can
>>>> selectively enable those programs before loading. This one if one choice
>>>> e.g. uprobe/uretprobe is not working, you can then try
>>>> uprobe.multi/uretprobe.multi.
>>> This is a good idea, but isn't making the upgradation built-in a better
>>> choice ?
>>> This way, anyone writing the program does not have to rewrite
>>> the same thing twice, keeping their programs pretty clean. This also
>>> moves the upgradation logic (which is probably going to be repeated multiple times)
>>> into the library which makes it easier for anyone to have something BPF
>>> Token compatible without having to write all this extra logic. Since "uprobe.multi"
>>> is compatible with "uprobe", I don't think anything will break as well.
>>> (The current breakages in the selftests are due to the patch being in
>>> nascent stages and I'll fix it after I get some feedback on my
>>> questions.)
>> I still feel this is a hack, esp. for libbpf. The libbpf provides various
>> APIs as the building block. Automatic upgrading inside libbpf does not
>> sound right. These upgrading thing should happen in applications.
>>
>>   From bpf program side, you can have progs for both uprobe and uprobe_multi.
>> You can have static function which can be used for both uprobe and uprobe_multi.
>> It should not be hard. Looks at bpf selftest, there are quite some programs
>> with prefix "?" which gives application a choice whether it should be
>> enabled or not during to kernel probing or other things.
>>
> Yeah, you can definitely handle this without needing to duplicate the
> logic in BPF code, but the idea here is to make uprobe work
> transparently inside user namespaced containers (assuming BPF token
> was provided), without having to explicitly accommodate this as a
> special mode.
>
> So while it can be seen as a bit of a hack, in practice whether you
> use uprobe or uprobe.multi doesn't really matter (they have equivalent
> features from BPF/kernel POV), but being able to just use
> SEC("uprobe") is great because you don't have to worry about old
> kernels not supporting uprobe.multi, plus you get automatic BPF token
> compatibility.

Okay. Thanks for explanation. uprobe.multi is a superset of uprobe.
So I guess it is okay for libbpf to upgrade from uprobe to uprobe.multi
if necessary.

>
> This is a bit harder for kprobes because singular kprobe can be
> installed at an offset, while kprobe.multi only support offset zero.
> But even with kprobe, I think it's worth trying to transparently make
> them BPF token-aware using a similar approach.


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-02-19  5:32 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-12 15:20 [RFC PATCH bpf-next 0/1] Upgrading uprobe and kprobe to their `multi` counterparts Varun R Mallya
2026-02-12 15:20 ` [RFC PATCH bpf-next 1/1] libbpf: Auto-upgrade uprobes to multi-uprobes when supported Varun R Mallya
2026-02-13  0:06   ` Yonghong Song
2026-02-13 17:22     ` Varun R Mallya
2026-02-14  5:25       ` Yonghong Song
2026-02-18 19:07         ` Andrii Nakryiko
2026-02-19  5:31           ` Yonghong Song
2026-02-18 19:03   ` Andrii Nakryiko
2026-02-18 19:07 ` [RFC PATCH bpf-next 0/1] Upgrading uprobe and kprobe to their `multi` counterparts Andrii Nakryiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox