perf/aux: Fix page UAF in map_range()
Subsystems (2)
A message that is not in the archive (sent off-list or not yet received): [email protected]
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().
--- kernel/events/core.c | 2 ++ 1 file changed, 2 insertions(+)
kernel/events/core.c +2 -0
@@ -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. *