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
next prev parent reply other threads:[~2026-04-21 8:56 UTC|newest]
Thread overview: 55+ 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 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 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-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 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-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-17 19:24 ` [PATCHv5 bpf-next 19/28] libbpf: Add btf_type_is_traceable_func function 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 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox