From: Peter Zijlstra <peterz@infradead.org>
To: Yilin Zhang <yilinzhang@moonshot.ai>
Cc: mingo@redhat.com, acme@kernel.org, namhyung@kernel.org,
kylebot@openai.com, david.lee@trailofbits.com,
linux-perf-users@vger.kernel.org, stable@vger.kernel.org,
Kimi Security Team <bug-report@moonshot.ai>,
Weiming Shi <shiweiming@moonshot.ai>
Subject: Re: [PATCH v3] perf: Fix use-after-free when perf mmap() revival races with the last munmap()
Date: Mon, 31 Aug 2026 15:39:15 +0200 [thread overview]
Message-ID: <20260831133915.GH4121339@noisy.programming.kicks-ass.net> (raw)
In-Reply-To: <20260831133152.1231045-1-yilinzhang@moonshot.ai>
On Mon, Aug 31, 2026 at 09:31:52PM +0800, Yilin Zhang wrote:
> perf_mmap_close() drops rb->mmap_count *without* holding
> event->mmap_mutex (the refcount_dec_and_test() right before the
> refcount_dec_and_mutex_lock() of event->mmap_count). A concurrent
> perf_mmap_rb() can slot its entire "revival" path into that window
> (perf_mmap holds event->mmap_mutex for its whole duration, including
> rb_alloc):
>
> munmap side (perf_mmap_close) mmap side (perf_mmap_rb)
> ----------------------------------- --------------------------------
> rb->mmap_count 1 -> 0 (no lock) (holds event->mmap_mutex)
> inc_not_zero(rb->mmap_count) fails
> ring_buffer_attach(event, NULL)
> rb_alloc() + attach new rb
> refcount_set(&event->mmap_count, 1)
> lock; event->mmap_count 1 -> 0
> ring_buffer_attach(event, NULL)
> ring_buffer_put() -> frees the *new* rb
>
> The revival's refcount_set(&event->mmap_count, 1) is an invisible
> 1 -> 1 write: the close frees the just-revived buffer although the
> other process still has it mapped -- a page-level use-after-free
> allowing local privilege escalation to root by any unprivileged user
> (default kernel.perf_event_paranoid=2).
>
> Swap the order of the two counter updates: event->mmap_count is
> dropped first via refcount_dec_and_mutex_lock(), so its 1 -> 0
> transition and the ring_buffer_attach() stay serialized with
> perf_mmap(). rb->mmap_count == 0 then implies every event using the
> buffer is detached already, so the result of the rb->mmap_count drop
> can gate the remaining teardown directly and detach_rest is no longer
> needed.
>
> An earlier fix for this race from Kyle Zeng and David Lee takes
> event->mmap_mutex around both counter updates [0]; here the not-last
> close stays lockless.
>
> Fixes: 59741451b49c ("perf: Identify the 0->1 transition for event::mmap_count")
> Link: https://lore.kernel.org/linux-perf-users/20260804060931.711308-1-david.lee@trailofbits.com/ [0]
> Cc: stable@vger.kernel.org # 6.18+
> Reported-by: Kimi Security Team <bug-report@moonshot.ai>
> Tested-by: Weiming Shi <shiweiming@moonshot.ai>
> Signed-off-by: Yilin Zhang <yilinzhang@moonshot.ai>
> ---
> v2: https://lore.kernel.org/linux-perf-users/91825d0335d2f7cc017ee23886ffeef44c1cc633.f061f277.0045.493a.937e.53242f19a7de@feishu.cn/T/#u
> v3: swap the counter drop order instead of serializing both under
> mmap_mutex; keeps the lockless fast path, drops detach_rest, and
> references the earlier independent fix [0]
I presume this means you and your AI agree with my pre-wakeup-juice
morning musings?
Also, you seem to have lost the 'helpful' comments that I drafted :-(
> kernel/events/core.c | 16 ++++++----------
> 1 file changed, 6 insertions(+), 10 deletions(-)
>
> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index a6c8e38a3110..f56f9d9e4f01 100644
> --- a/kernel/events/core.c
> +++ b/kernel/events/core.c
> @@ -7029,7 +7029,6 @@ static void perf_mmap_close(struct vm_area_struct *vma)
> mapped_f unmapped = get_mapped(event, event_unmapped);
> struct perf_buffer *rb = ring_buffer_get(event);
> struct user_struct *mmap_user = rb->mmap_user;
> - bool detach_rest = false;
>
> /* FIXIES vs perf_pmu_unregister() */
> if (unmapped)
> @@ -7060,17 +7059,14 @@ static void perf_mmap_close(struct vm_area_struct *vma)
> mutex_unlock(&rb->aux_mutex);
> }
>
> - if (refcount_dec_and_test(&rb->mmap_count))
> - detach_rest = true;
> -
> - if (!refcount_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex))
> - goto out_put;
> -
> - ring_buffer_attach(event, NULL);
> - mutex_unlock(&event->mmap_mutex);
> + if (refcount_dec_and_mutex_lock(&event->mmap_count,
> + &event->mmap_mutex)) {
> + ring_buffer_attach(event, NULL);
> + mutex_unlock(&event->mmap_mutex);
> + }
>
> /* If there's still other mmap()s of this buffer, we're done. */
> - if (!detach_rest)
> + if (!refcount_dec_and_test(&rb->mmap_count))
> goto out_put;
>
> /*
> --
> 2.43.0
next prev parent reply other threads:[~2026-08-31 13:39 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 13:31 [PATCH v3] perf: Fix use-after-free when perf mmap() revival races with the last munmap() Yilin Zhang
2026-08-31 13:39 ` Peter Zijlstra [this message]
2026-08-31 15:13 ` Yilin Zhang
2026-08-31 13:50 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260831133915.GH4121339@noisy.programming.kicks-ass.net \
--to=peterz@infradead.org \
--cc=acme@kernel.org \
--cc=bug-report@moonshot.ai \
--cc=david.lee@trailofbits.com \
--cc=kylebot@openai.com \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=shiweiming@moonshot.ai \
--cc=stable@vger.kernel.org \
--cc=yilinzhang@moonshot.ai \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.