Re: [PATCH v2] perf: Fix mmap replacement ring lifetime race

[email protected] Tue, 04 Aug 2026 06:34:23 +0000
Newsgroups org.kernel.vger.linux-perf-users
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 2 potential issue(=
s) to consider:

Pre-existing issues:
- [High] Cross-mm pinned_vm and locked_vm accounting mismatch when main buf=
fer and AUX buffer are mapped by different processes.
- [High] Error path in perf_mmap leaks ring buffer mmap counts and unaccoun=
ts wrong memory size for AUX mmaps on redirected events.
--

--- Patch [1]: [PATCH v2] perf: Fix mmap replacement ring lifetime race ---
commit 454633461f89e2fe1c245d071d92eff5dc95e16e
Author: Kyle Zeng <[email protected]>
Subject: perf: Fix mmap replacement ring lifetime race

perf_mmap_close() drops the ring-local mmap_count before serializing
with perf_mmap() through event->mmap_mutex. This patch takes
event->mmap_mutex before updating either count, ensuring the transitions
are atomic with respect to perf_mmap().

Link: https://lore.kernel.org/all/20260731120401.558858-1-david.lee@trailof=
bits.com/

> diff --git a/kernel/events/core.c b/kernel/events/core.c
> index 51c1200ea3fdd..cce24be2d1f28 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);
>  	}
> =20
> +	/*
> +	 * 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 =3D true;
> =20
> -	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;

[Severity: High]
This is a pre-existing issue, but does this mmap_count logic lead to a
cross-mm pinned_vm and locked_vm accounting mismatch when the main buffer
and AUX buffer are mapped by different processes?

If Process A maps the main ring buffer of an event, charging its VMA's
pinned_vm, and the event fd is shared with Process B which maps the AUX
buffer, rb->mmap_count tracks both mappings globally.

If Process A unmaps the main buffer first, the following evaluates to false:

	if (refcount_dec_and_test(&rb->mmap_count))
		detach_rest =3D true;

The mmap_count decrements but does not reach zero, skipping the later call
to perf_mmap_unaccount().

When Process B later unmaps the AUX buffer, rb->mmap_count reaches zero
and perf_mmap_unaccount() is called with Process B's VMA near the end of
perf_mmap_close():

	/*
	 * Aside from that, this buffer is 'fully' detached and unmapped,
	 * undo the VM accounting.
	 */
	perf_mmap_unaccount(vma, rb);

Does this unaccount the main buffer's size from Process B's limits,
underflowing Process B's pinned_vm while permanently leaking the limits for
Process A?

> +	}
> =20
>  	ring_buffer_attach(event, NULL);
>  	mutex_unlock(&event->mmap_mutex);

[Severity: High]
This is also a pre-existing issue, but looking at the related error path in
perf_mmap(), could it leak ring buffer mmap counts and unaccount the wrong
memory size for AUX mmaps on redirected events?

When mmap() is called for the AUX buffer of a redirected event,
perf_mmap_aux() increments rb->mmap_count, rb->aux_mmap_count, and sets
event->mmap_count to 1.

If map_range() fails due to an error, the cleanup path in perf_mmap()
evaluates this:

		if (refcount_read(&event->mmap_count) =3D=3D 1) {
			/*
			 * Minimal perf_mmap_close(); there can't be AUX or
			 * other events on account of this being the first.
			 */
			mapped =3D get_mapped(event, event_unmapped);
			if (mapped)
				mapped(event, vma->vm_mm);
			perf_mmap_unaccount(vma, event->rb);
			ring_buffer_attach(event, NULL);
			refcount_set(&event->mmap_count, 0);
			return ret;
		}

Because event->mmap_count is 1, it assumes this is an exclusive main buffer
mmap. It then calls perf_mmap_unaccount(), which unaccounts the main
buffer's size instead of the AUX buffer's size, potentially underflowing the
caller's pinned_vm.

Additionally, it skips decrementing rb->mmap_count and rb->aux_mmap_count
while detaching the event from the ring buffer. Could this cause a
persistent memory leak of ring buffer pages and break event redirection?

--=20
Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260804060931.7113=
[email protected]?part=3D1