diff options
| author | Namhyung Kim <namhyung@kernel.org> | 2026-09-20 16:16:39 -0700 |
|---|---|---|
| committer | Peter Zijlstra <peterz@infradead.org> | 2026-09-23 11:48:35 +0200 |
| commit | cca4980630b3c7a85f53cb43c6018184ce5d4e37 (patch) | |
| tree | bd323431def4c1983cf8d6b422a63e2ccfde47d1 | |
| parent | d4d9ccbad527af115b8e83a7a2b74785c0e8dfea (diff) | |
| download | linux-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.c | 2 |
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; } |
