All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiri Olsa <olsajiri@gmail.com>
To: bot+bpf-ci@kernel.org
Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
	bpf@vger.kernel.org, linux-trace-kernel@vger.kernel.org,
	kafai@fb.com, eddyz87@gmail.com, songliubraving@fb.com,
	yhs@fb.com, menglong8.dong@gmail.com, rostedt@kernel.org,
	martin.lau@kernel.org, yonghong.song@linux.dev, clm@meta.com,
	ihor.solodrai@linux.dev
Subject: Re: [PATCHv5 bpf-next 12/28] bpf: Add bpf_trampoline_multi_attach/detach functions
Date: Tue, 21 Apr 2026 10:56:26 +0200	[thread overview]
Message-ID: <aec7utuKyC1YtpYv@krava> (raw)
In-Reply-To: <0fefc9ca058787dddef9c125163a0182c6ddd1f47d2754f2ddbb5a82bfc9126a@mail.kernel.org>

On Sat, Apr 18, 2026 at 06:10:57AM +0000, bot+bpf-ci@kernel.org wrote:

SNIP

> > +		err = __bpf_trampoline_link_prog(&mnode->node, mnode->trampoline, NULL,
> > +						 &trampoline_multi_ops, data);
> > +		if (err) {
> > +			rollback_cnt = i;
> > +			goto rollback_unlink;
> > +		}
> > +	}
> 
> When user-provided ids[] contains duplicate BTF IDs (or distinct IDs
> that resolve to the same trampoline key), multiple nodes point to the
> same struct bpf_trampoline. The link loop above then calls
> bpf_trampoline_multi_attach_init() more than once on that trampoline,
> overwriting the saved old_image with the newly assigned cur_image from
> the previous iteration.
> 
> Scenario with ids[0] == ids[1] and trampoline X starting with OLD_X:
> 
>   i=0: attach_init(X) saves old_image=OLD_X
>        __bpf_trampoline_link_prog() -> modify_fentry_multi() sets
>        X->cur_image=NEW_X (OLD_X refcount not dropped, intent is for
>        multi_attach_free() to release it later).
> 
>   i=1: attach_init(X) re-runs on the same trampoline and overwrites
>        old_image=NEW_X (the only saved reference to OLD_X is lost).
>        __bpf_trampoline_link_prog() returns -EBUSY (duplicate prog).
>        rollback_cnt=i=1; goto rollback_unlink.
> 
>   rollback_unlink calls bpf_trampoline_multi_attach_rollback(X) once:
> 
>     static void bpf_trampoline_multi_attach_rollback(struct bpf_trampoline *tr)
>     {
>         if (tr->cur_image)
>             bpf_tramp_image_put(tr->cur_image);    /* puts NEW_X */
>         tr->cur_image = tr->multi_attach.old_image;  /* = NEW_X (stale!) */
>         ...
>     }
> 
> Result: OLD_X leaks (its refcount is never decremented). cur_image
> points at NEW_X which was just released via bpf_tramp_image_put().
> 
> The rollback_put loop calls bpf_trampoline_put() which frees the
> trampoline (prog was removed, progs_hlist is empty), so the dangling
> NEW_X pointer is cleaned up. However OLD_X remains allocated via
> module_alloc forever.
> 
> Reachability: commit dbf2afe2f603 ("bpf: Add support for tracing multi
> link") copies ids[] from userspace without deduplication. The series'
> selftests (commit 411fb40d4b2a "selftests/bpf: Add tracing multi attach
> fails test", 'fail#7 (kernel) attach with duplicate id') exercise this
> path explicitly, expecting -EBUSY.
> 
> The trigger requires the shared trampoline to already have cur_image !=
> NULL when the first iteration runs (i.e., another program attached to
> the same function), so modify_fentry_multi() is used and there is a
> real OLD_X to leak. The first-time-attach case (register_fentry_multi,
> OLD_X=NULL) only leaves cur_image dangling briefly until the trampoline
> is freed.
> 
> Can the code save old_image/old_flags once per trampoline instead of
> once per node, or store them per-mnode to avoid overwriting the saved
> state?

I think it's better to ensure the btf ids array does not contain
duplicate entries.. wil check

> 
> > +
> > +	rollback_cnt = link->nodes_cnt;
> > +	if (ftrace_hash_count(data->reg)) {
> > +		err = update_ftrace_direct_add(&direct_ops, data->reg);
> > +		if (err)
> > +			goto rollback_unlink;
> > +	}
> > +
> > +	if (ftrace_hash_count(data->modify)) {
> > +		err = update_ftrace_direct_mod(&direct_ops, data->modify, true);
> > +		if (err) {
> > +			WARN_ON_ONCE(update_ftrace_direct_del(&direct_ops, data->reg));
> > +			                                                       ^^^^^^^^^^
> > +			goto rollback_unlink;
> > +		}
> > +	}
> 
> The recovery path unconditionally calls update_ftrace_direct_del with
> data->reg, but update_ftrace_direct_add is only invoked when data->reg
> is non-empty:
> 
>     if (ftrace_hash_count(data->reg)) {
>         err = update_ftrace_direct_add(&direct_ops, data->reg);
>         ...
>     }
> 
> When update_ftrace_direct_mod fails and all trampolines already had
> cur_image != NULL (so nothing was added to data->reg),
> update_ftrace_direct_del is called with an empty hash.
> update_ftrace_direct_del at kernel/trace/ftrace.c:6511 checks:
> 
>     if (!hash_count(hash))
>         return -EINVAL;
> 
> so it returns -EINVAL and the WARN_ON_ONCE fires spuriously. Should
> this guard match the add path?
> 
>     if (ftrace_hash_count(data->reg))
>         WARN_ON_ONCE(update_ftrace_direct_del(&direct_ops, data->reg));

yes, replied in the other email

SNIP

> > +int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 btf_id,
> > +				  struct bpf_attach_target_info *tgt_info)
> > +{
> > +	const struct btf_type *t;
> > +	unsigned long addr;
> > +	const char *tname;
> > +	int err;
> > +
> > +	if (!btf_id || !btf)
> > +		return -EINVAL;
> > +
> > +	/* Check noreturn attachment. */
> > +	if (prog->expected_attach_type == BPF_TRACE_FEXIT_MULTI ||
> > +	     btf_id_set_contains(&noreturn_deny, btf_id))
> > +		return -EINVAL;
> 
> Operator precedence issue in the noreturn check. The two subexpressions
> are joined by ||, so the function returns -EINVAL whenever EITHER:
> 
>   (a) the program's expected_attach_type is BPF_TRACE_FEXIT_MULTI
>       (regardless of whether btf_id targets a noreturn function), OR
> 
>   (b) the btf_id is in the noreturn_deny set (regardless of program
>       attach type, rejecting FENTRY_MULTI attachments to noreturn
>       functions as well).
> 
> The intent is to reject only FEXIT-like multi attachments to noreturn
> functions. The non-multi path in check_attach_btf_id() correctly uses
> &&:
> 
>     } else if ((prog->expected_attach_type == BPF_TRACE_FEXIT ||
>                prog->expected_attach_type == BPF_TRACE_FSESSION ||
>                prog->expected_attach_type == BPF_MODIFY_RETURN) &&
>                btf_id_set_contains(&noreturn_deny, btf_id)) {
>         ...
>         return -EINVAL;
>     }
> 
> Consequences once bpf_trampoline_multi_attach() is wired up from
> user-space (commit dbf2afe2f603 "bpf: Add support for tracing multi
> link"):
> 
>   1. Every BPF_TRACE_FEXIT_MULTI program attach request is rejected
>      with -EINVAL, even when the target function returns normally. This
>      disables BPF_TRACE_FEXIT_MULTI entirely.
> 
>   2. Any tracing_multi program (including BPF_TRACE_FENTRY_MULTI) is
>      rejected when its btf_id is in noreturn_deny (do_exit,
>      __x64_sys_exit, make_task_dead, etc.). Rejecting FENTRY_MULTI on
>      noreturn functions is an unintended behavioral restriction.
> 
> Forward search in the git range shows commit bfcad202ce99 ("bpf: Add
> support for tracing_multi link session") rewrites this hunk to:
> 
>     if ((prog->expected_attach_type == BPF_TRACE_FEXIT_MULTI ||
>          prog->expected_attach_type == BPF_TRACE_FSESSION_MULTI) &&
>          btf_id_set_contains(&noreturn_deny, btf_id))
>         return -EINVAL;
> 
> The parenthesization + && in the later commit confirms this is a
> precedence bug. Should this use && instead?

yes, replied in the other email

jirka

  reply	other threads:[~2026-04-21  8:56 UTC|newest]

Thread overview: 71+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-17 19:24 [PATCHv5 bpf-next 00/28] bpf: tracing_multi link Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 01/28] ftrace: Add ftrace_hash_count function Jiri Olsa
2026-04-18  0:58   ` sashiko-bot
2026-04-21  8:54     ` Jiri Olsa
2026-04-18  6:10   ` bot+bpf-ci
2026-04-17 19:24 ` [PATCHv5 bpf-next 02/28] ftrace: Add ftrace_hash_remove function Jiri Olsa
2026-04-18  6:10   ` bot+bpf-ci
2026-04-21  8:54     ` Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 03/28] ftrace: Add add_ftrace_hash_entry function Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 04/28] bpf: Use mutex lock pool for bpf trampolines Jiri Olsa
2026-04-17 20:10   ` bot+bpf-ci
2026-04-21  8:54     ` Jiri Olsa
2026-04-18  3:52   ` sashiko-bot
2026-04-21  8:55     ` Jiri Olsa
2026-04-24 11:24       ` Jiri Olsa
2026-04-18  6:49   ` bot+bpf-ci
2026-04-17 19:24 ` [PATCHv5 bpf-next 05/28] bpf: Add struct bpf_trampoline_ops object Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 06/28] bpf: Move trampoline image setup into bpf_trampoline_ops callbacks Jiri Olsa
2026-04-17 20:10   ` bot+bpf-ci
2026-04-21  8:55     ` Jiri Olsa
2026-05-25 20:05       ` Jiri Olsa
2026-04-18  6:10   ` bot+bpf-ci
2026-04-17 19:24 ` [PATCHv5 bpf-next 07/28] bpf: Add bpf_trampoline_add/remove_prog functions Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 08/28] bpf: Add struct bpf_tramp_node object Jiri Olsa
2026-04-17 20:22   ` bot+bpf-ci
2026-04-18  6:10   ` bot+bpf-ci
2026-04-21  8:55     ` Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 09/28] bpf: Factor fsession link to use struct bpf_tramp_node Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 10/28] bpf: Add multi tracing attach types Jiri Olsa
2026-04-17 20:22   ` bot+bpf-ci
2026-04-21  8:55     ` Jiri Olsa
2026-04-18  4:09   ` sashiko-bot
2026-04-21  8:55     ` Jiri Olsa
2026-04-18  6:49   ` bot+bpf-ci
2026-04-21  8:56     ` Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 11/28] bpf: Move sleepable verification code to btf_id_allow_sleepable Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 12/28] bpf: Add bpf_trampoline_multi_attach/detach functions Jiri Olsa
2026-04-17 20:22   ` bot+bpf-ci
2026-04-21  8:56     ` Jiri Olsa
2026-04-18  6:10   ` bot+bpf-ci
2026-04-21  8:56     ` Jiri Olsa [this message]
2026-04-17 19:24 ` [PATCHv5 bpf-next 13/28] bpf: Add support for tracing multi link Jiri Olsa
2026-04-18  8:58   ` sashiko-bot
2026-04-21  8:56     ` Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 14/28] bpf: Add support for tracing_multi link cookies Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 15/28] bpf: Add support for tracing_multi link session Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 16/28] bpf: Add support for tracing_multi link fdinfo Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 17/28] libbpf: Add bpf_object_cleanup_btf function Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 18/28] libbpf: Add bpf_link_create support for tracing_multi link Jiri Olsa
2026-04-18  3:50   ` sashiko-bot
2026-04-21  8:56     ` Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 19/28] libbpf: Add btf_type_is_traceable_func function Jiri Olsa
2026-04-18  3:40   ` sashiko-bot
2026-04-21  8:56     ` Jiri Olsa
2026-04-18  5:59   ` bot+bpf-ci
2026-04-17 19:24 ` [PATCHv5 bpf-next 20/28] libbpf: Add support to create tracing multi link Jiri Olsa
2026-04-18  6:10   ` bot+bpf-ci
2026-04-21  8:57     ` Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 21/28] selftests/bpf: Add tracing multi skel/pattern/ids attach tests Jiri Olsa
2026-04-17 20:10   ` bot+bpf-ci
2026-04-21  8:54     ` Jiri Olsa
2026-04-18  3:34   ` sashiko-bot
2026-04-21  8:57     ` Jiri Olsa
2026-04-18  6:10   ` bot+bpf-ci
2026-04-17 19:24 ` [PATCHv5 bpf-next 22/28] selftests/bpf: Add tracing multi skel/pattern/ids module " Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 23/28] selftests/bpf: Add tracing multi intersect tests Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 24/28] selftests/bpf: Add tracing multi cookies test Jiri Olsa
2026-04-17 19:24 ` [PATCHv5 bpf-next 25/28] selftests/bpf: Add tracing multi session test Jiri Olsa
2026-04-17 19:25 ` [PATCHv5 bpf-next 26/28] selftests/bpf: Add tracing multi attach fails test Jiri Olsa
2026-04-17 19:25 ` [PATCHv5 bpf-next 27/28] selftests/bpf: Add tracing multi attach benchmark test Jiri Olsa
2026-04-17 19:25 ` [PATCHv5 bpf-next 28/28] selftests/bpf: Add tracing multi attach rollback tests Jiri Olsa

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aec7utuKyC1YtpYv@krava \
    --to=olsajiri@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bot+bpf-ci@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=clm@meta.com \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=ihor.solodrai@linux.dev \
    --cc=kafai@fb.com \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=martin.lau@kernel.org \
    --cc=menglong8.dong@gmail.com \
    --cc=rostedt@kernel.org \
    --cc=songliubraving@fb.com \
    --cc=yhs@fb.com \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.