[PATCH v2] perf: Fix mmap replacement ring lifetime race
David Lee <[email protected]> Tue, 4 Aug 2026 06:09:31 +0000
| Newsgroups | org.kernel.vger.linux-perf-users,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Kyle Zeng <[email protected]> perf_mmap_close() drops the ring-local mmap_count before serializing with perf_mmap() through event->mmap_mutex. When the last mapping is being closed, a concurrent mmap can therefore observe a nonzero event->mmap_count and a zero rb->mmap_count. In that case perf_mmap_rb() detaches the old ring, installs a replacement, and resets event->mmap_count to one. The old close then consumes that replacement count and detaches the new ring. Its pages can consequently be freed while the replacement VMA still maps their PFNs. Take event->mmap_mutex before updating either count. This makes the ring-local and event-global count transitions atomic with respect to perf_mmap(), so a replacement cannot be installed until the old close has detached its ring. Fixes: 59741451b49c ("perf: Identify the 0->1 transition for event::mmap_count") Assisted-by: Codex:gpt-5.6-sol Codex:gpt-5.5-cyber Signed-off-by: Kyle Zeng <[email protected]> Co-developed-by: David Lee <[email protected]> Signed-off-by: David Lee <[email protected]> --- Changes in v2: - Restore Kyle Zeng as the patch author and correct the sign-off chain. - Move the research credit below the commit-message separator. v1: https://lore.kernel.org/all/[email protected]/ Bug found and triaged by OpenAI Security Research and validated by Trail of Bits. Trail of Bits has a reproducer for this bug that triggers a kernel panic and can share if needed. kernel/events/core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index ba5bd6a78..f93327c76 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7053,11 +7053,19 @@ static void perf_mmap_close(struct vm_area_struct *vma) mutex_unlock(&rb->aux_mutex); } + /* + * Serialize both count updates with perf_mmap() so they cannot + * refer to different ring buffer generations. + */ + mutex_lock(&event->mmap_mutex); + if (refcount_dec_and_test(&rb->mmap_count)) detach_rest = true; - if (!refcount_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) + if (!refcount_dec_and_test(&event->mmap_count)) { + mutex_unlock(&event->mmap_mutex); goto out_put; + } ring_buffer_attach(event, NULL); mutex_unlock(&event->mmap_mutex); -- 2.53.0