[tip: perf/urgent] perf: Fix UAF in attach_task_ctx_data()

From: tip-bot2 for Lee Jia Jie

Date: Sat Oct 24 2026 - 07:34:14 EST


The following commit has been merged into the perf/urgent branch of tip:

Commit-ID: 64ea0e8226ed98d14e0231af1beae10c3d5c043a
Gitweb: https://git.kernel.org/tip/64ea0e8226ed98d14e0231af1beae10c3d5c043a
Author: Lee Jia Jie <mkofdwu@xxxxxxxxx>
AuthorDate: Thu, 22 Oct 2026 20:58:48 +08:00
Committer: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
CommitterDate: Fri, 23 Oct 2026 19:09:52 +02:00

perf: Fix UAF in attach_task_ctx_data()

struct perf_ctx_data is RCU protected. In attach_task_ctx_data(),
the cmpxchg retry loop runs without holding rcu_read_lock(). If
task->perf_ctx_data already holds a pointer (call it `cd1`), then
try_cmpxchg(&task->perf_ctx_data, &old, cd) fails and loads old = cd1.
A concurrent thread running detach_task_ctx_data() could free cd1
leading to UAF and further memory corruption when cd1 is reclaimed by
another thread.

CPU 0: attach_task_ctx_data() CPU 1: detach_task_ctx_data()
----------------------------- -----------------------------
try_cmpxchg(&task->perf_ctx_data,
&old, cd2)
-> fails; loads old = cd1

refcount_dec_and_test(&cd1->refcount)
try_cmpxchg(&task->perf_ctx_data,
&cd1, NULL)
perf_free_ctx_data_rcu(cd1)
[ RCU grace period -> kfree(cd1) ]

refcount_inc_not_zero(&old->refcount)
-> UAF on freed cd1

Fix this by taking rcu_read_lock across the race window.

Fixes: 506e64e710ff ("perf: attach/detach PMU specific data")
Signed-off-by: Lee Jia Jie <mkofdwu@xxxxxxxxx>
Signed-off-by: Peter Zijlstra (Intel) <peterz@xxxxxxxxxxxxx>
---
kernel/events/core.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/kernel/events/core.c b/kernel/events/core.c
index a9ee911..e248f34 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5434,6 +5434,9 @@ attach_task_ctx_data(struct task_struct *task, struct kmem_cache *ctx_cache,
return -ENOMEM;

for (;;) {
+ /* Protect @old until inc_not_zero(&old->refcount) */
+ guard(rcu)();
+
if (try_cmpxchg(&task->perf_ctx_data, &old, cd)) {
if (old)
perf_free_ctx_data_rcu(old);