[PATCH bpf-next v2 3/3] bpf: Fix prog UAF in __bpf_trampoline_unlink_prog() on update failure

"Hui Zhu" <[email protected]> Wed, 5 Aug 2026 12:04:08 +0800
Newsgroups org.kernel.vger.linux-trace-kernel,org.kernel.vger.bpf,org.kernel.vger.linux-kernel
Message-ID <0e91fde8222bcfc0b318cfa2dcc3e06cc476c1a2.1785902527.git.zhuhui@kylinos.cn>
From: Hui Zhu <[email protected]>

If bpf_trampoline_update() fails in __bpf_trampoline_unlink_prog(),
cur_image is unchanged and still has node->link->prog's call baked
into it. All callers only WARN_ON_ONCE() the failure and then
unconditionally free the prog -- a UAF.

Fix: on failure, pin node->link->prog onto tr->cur_image via the
pinned_prog mechanism, so it outlives the link. This covers both the
multi (bpf_trampoline_multi_detach) and non-multi
(bpf_tracing_link_release, bpf_shim_tramp_link_release) paths.

Fixes: aef4dfa790b2 ("bpf: Add bpf_trampoline_multi_attach/detach functions")
Signed-off-by: Hui Zhu <[email protected]>
---
 kernel/bpf/trampoline.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 7fac27374ece..6071261fd66b 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -991,7 +991,25 @@ static int __bpf_trampoline_unlink_prog(struct bpf_tramp_node *node,
 		return err;
 	}
 	bpf_trampoline_remove_prog(tr, node);
-	return bpf_trampoline_update(tr, true /* lock_direct_mutex */, ops, data);
+	err = bpf_trampoline_update(tr, true /* lock_direct_mutex */, ops, data);
+	/*
+	 * If the update above failed, tr->cur_image is unchanged, i.e. ftrace
+	 * (or the direct jump, for the standard ops) is still actually
+	 * calling into an image with node->link->prog's call baked into its
+	 * machine code, even though this unlink is being reported as failed.
+	 * Every caller of this function only WARN_ON_ONCE()'s a failure here
+	 * and then unconditionally frees the underlying bpf_prog, which
+	 * would be a use-after-free the next time that image is executed.
+	 * Pin an extra reference on the prog onto tr->cur_image so it
+	 * outlives this link, mirroring the fix applied to
+	 * bpf_trampoline_multi_attach()/bpf_trampoline_multi_detach().
+	 */
+	if (err && tr->cur_image) {
+		WARN_ON_ONCE(tr->cur_image->pinned_prog);
+		bpf_prog_inc(node->link->prog);
+		tr->cur_image->pinned_prog = node->link->prog;
+	}
+	return err;
 }
 
 /* bpf_trampoline_unlink_prog() should never fail. */
-- 
2.53.0