Skip to contentSkip to conversation

perf/aux: Fix page UAF in map_range()

PATCH New Lee Jia Jie 1 participant 1 message Jul 10 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

#1 lore raw

PATCH 1/1 perf/aux: Fix page UAF in map_range()

map_range() reads rb->aux_pages[], rb->aux_nr_pages and rb->aux_pgoff via perf_mmap_to_page() while holding only event->mmap_mutex. Those fields are serialized by rb->aux_mutex, and mmap_mutex is per event.

Thus, two events sharing one rb via PERF_EVENT_IOC_SET_OUTPUT can race rb_alloc_aux() with map_range(), leading to a page-UAF scenario as follows:

CPU 0 CPU 1
===============================================================
rb_alloc_aux() map_range()
[1]: allocate rb->aux_pages[0]
[2]: rb->aux_nr_pages++

                                [3]: perf_mmap_to_page()
                                       returns rb->aux_pages[0]
                                [4]: map it as VM_PFNMAP

[5]: rb->aux_pgoff = 1

munmap the page
[6]: free rb->aux_pages[0]
===============================================================

Pages mapped as VM_PFNMAP have no refcount protection, so CPU 1 holds a mapping to a freed physical frame.

Fix this by taking rb->aux_mutex across the page walk in map_range().

Fixes b709eb872e19 ("perf: map pages in advance")
---
 kernel/events/core.c | 2 ++
 1 file changed, 2 insertions(+)
kernel/events/core.c +2 -0
index d7f3e2c2ecb1..ba5bd6a78fe7 100644
@@ -7150,6 +7150,8 @@ static int map_range(struct perf_buffer *rb, struct vm_area_struct *vma)
 	int err = 0;
 	unsigned long pagenum;
 
+	guard(mutex)(&rb->aux_mutex);
+
 	/*
 	 * We map this as a VM_PFNMAP VMA.
 	 *