summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNamhyung Kim <namhyung@kernel.org>2026-09-20 16:16:39 -0700
committerPeter Zijlstra <peterz@infradead.org>2026-09-23 11:48:35 +0200
commitcca4980630b3c7a85f53cb43c6018184ce5d4e37 (patch)
treebd323431def4c1983cf8d6b422a63e2ccfde47d1
parentd4d9ccbad527af115b8e83a7a2b74785c0e8dfea (diff)
downloadlinux-stable-cca4980630b3c7a85f53cb43c6018184ce5d4e37.tar.gz
linux-stable-cca4980630b3c7a85f53cb43c6018184ce5d4e37.zip
perf/core: Fix a refcount leak in attach_perf_ctx_data()
The attach_perf_ctx_data() can race on global and !global cases. The global case is protected by global_ctx_data_rwsem and shares a single reference count using perf_ctx_data.global field. But when it races with !global case, it may miss to set the global field and result in a reference count leak. CPU1 CPU2 ---------------------------------------------------------------- attach_task_ctx_data(.global=1) attach_task_ctx_data(.global=0) cd1 = alloc_perf_ctx_data(); cd2 = alloc_perf_ctx_data(); // { .global = 0, .refcount = 1 }; try_cmpxchg(); // success, // task->perf_ctx_data = cd2 try_cmpxhg(); // fail; old = cd2 refcount_inc_not_zero(&old->refcount); // success // old.refcount = 2 free_perf_ctx_data(cd1); Then later detach_global_ctx_data() will see the data but it's not marked as global, so it won't call detach_task_ctx_data(). Fixes: 506e64e710ff ("perf: attach/detach PMU specific data") Assisted-by: Sashiko.dev:Gemini-3.1-pro Signed-off-by: Namhyung Kim <namhyung@kernel.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/20260920231639.11910-1-namhyung@kernel.org
-rw-r--r--kernel/events/core.c2
1 files changed, 2 insertions, 0 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c
index db7b76d6b..e180134ba 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5454,6 +5454,8 @@ attach_task_ctx_data(struct task_struct *task, struct kmem_cache *ctx_cache,
}
if (refcount_inc_not_zero(&old->refcount)) {
+ if (global)
+ old->global = true;
free_perf_ctx_data(cd); /* unused */
return 0;
}