BPF List
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Hui Zhu" <hui.zhu@linux.dev>
Cc: bpf@vger.kernel.org
Subject: Re: [PATCH v4] bpf: Fix UAF in bpf_trampoline_multi_attach_free on update failure
Date: Mon, 10 Aug 2026 06:27:22 +0000	[thread overview]
Message-ID: <20260810062723.555121F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260810061329.224276-1-hui.zhu@linux.dev>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] Ignoring errors from `__bpf_trampoline_unlink_prog()` during detach leads to a Use-After-Free of the BPF program.
- [High] Ignoring errors from `update_ftrace_direct_del/mod()` during detach or attach rollback leads to a Use-After-Free of the trampoline image.
--

--- Patch [1]: [PATCH v4] bpf: Fix UAF in bpf_trampoline_multi_attach_free on update failure ---
commit 12049949b83ebddc54a6fdad57fa0b635706dbda
Author: Hui Zhu <zhuhui@kylinos.cn>
bpf: Fix UAF in bpf_trampoline_multi_attach_free on update failure

When bpf_trampoline_update() fails before modify_fentry_multi() or
unregister_fentry_multi() is called, cur_image is unchanged and ftrace still
calls into it. Freeing old_image in that case causes a UAF. This patch only
frees old_image when it differs from cur_image, and makes
bpf_trampoline_multi_detach() return void.

> diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> index e07af35ed0402..90b70ea0d3709 100644
> --- a/kernel/bpf/trampoline.c
> +++ b/kernel/bpf/trampoline.c
> @@ -1632,7 +1632,17 @@ static void bpf_trampoline_multi_attach_init(struct bpf_trampoline *tr)
>  
>  static void bpf_trampoline_multi_attach_free(struct bpf_trampoline *tr)
>  {
> -	if (tr->multi_attach.old_image)
> +	/*
> +	 * Only free old_image if it is no longer the active image.
> +	 * When bpf_trampoline_update() fails before modify_fentry_multi()/
> +	 * unregister_fentry_multi() is called, cur_image is unchanged
> +	 * (cur_image == old_image) and ftrace still points to it. Freeing
> +	 * it would cause a UAF when ftrace calls into the freed memory.
> +	 * On success, cur_image is either a new image or NULL, so
> +	 * old_image != cur_image means the image is stale.
> +	 */
> +	if (tr->multi_attach.old_image &&
> +	    tr->multi_attach.old_image != tr->cur_image)

[Severity: High]
This is a pre-existing issue, but does this condition still allow a
use-after-free of the trampoline image if ftrace updates fail?

If __bpf_trampoline_unlink_prog() succeeds in bpf_trampoline_multi_detach(),
the active image is updated and old_image != cur_image will be true.

However, if the subsequent update to ftrace fails due to memory pressure:

    if (ftrace_hash_count(data->unreg))
        WARN_ON_ONCE(update_ftrace_direct_del(&direct_ops, data->unreg));

ftrace will return an error without updating its hooks, leaving ftrace
pointing to the original active image (old_image).

Since old_image != cur_image is true, won't bpf_trampoline_multi_attach_free()
then free old_image while ftrace is still executing it?

A similar issue appears to exist during error rollback in
kernel/bpf/trampoline.c:bpf_trampoline_multi_attach() if
update_ftrace_direct_del() fails, leaving ftrace executing newly allocated
images that are subsequently freed.

>  		bpf_tramp_image_put(tr->multi_attach.old_image);
>  
>  	tr->multi_attach.old_image = NULL;

[ ... ]

> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index 891897f8a1b35..29260951aa871 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -3687,7 +3687,7 @@ static void bpf_tracing_multi_link_release(struct bpf_link *link)
>  	struct bpf_tracing_multi_link *tr_link =
>  		container_of(link, struct bpf_tracing_multi_link, link);
>  
> -	WARN_ON_ONCE(bpf_trampoline_multi_detach(link->prog, tr_link));
> +	bpf_trampoline_multi_detach(link->prog, tr_link);

[Severity: High]
This is a pre-existing issue, but does dropping the program reference here
lead to a use-after-free if __bpf_trampoline_unlink_prog() fails?

In kernel/bpf/trampoline.c:bpf_trampoline_multi_detach(), if unlink fails:

    err = __bpf_trampoline_unlink_prog(&mnode->node, mnode->trampoline, NULL,
                                       &trampoline_multi_ops, data);
    WARN_ONCE(err, "__bpf_trampoline_unlink_prog failed: %d\n", err);

the detach path ignores the error. The ftrace direct hook remains active and
the active trampoline image remains unchanged, keeping its hardcoded call
to the BPF program.

Since bpf_tracing_multi_link_release() proceeds to drop the link's reference
to the BPF program, if the program's refcount drops to zero and it is freed,
could ftrace continue to jump into the freed program?

>  }
>  
>  static void bpf_tracing_multi_link_dealloc(struct bpf_link *link)

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260810061329.224276-1-hui.zhu@linux.dev?part=1

  reply	other threads:[~2026-08-10  6:27 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-10  6:13 [PATCH v4] bpf: Fix UAF in bpf_trampoline_multi_attach_free on update failure Hui Zhu
2026-08-10  6:27 ` sashiko-bot [this message]
2026-08-10  7:16 ` bot+bpf-ci

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=20260810062723.555121F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=hui.zhu@linux.dev \
    --cc=sashiko-reviews@lists.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