Headline
fastrpc_mmap_find Information Leak
An incorrect searching algorithm in fastrpc_mmap_find can lead to kernel address space information leaks.
Inside of fastrpc_mmap_find, there exists the following code to search for ADSP_MMAP_HEAP_ADDR or ADSP_MMAP_REMOTE_HEAP_ADDR allocations:hlist_for_each_entry_safe(map, n, &me->maps, hn) { if (va >= map->va && va + len <= map->va + map->len && map->fd == fd) { if (refs) { if (map->refs + 1 == INT_MAX) { spin_unlock_irqrestore(&me->hlock, irq_flags); return -ETOOMANYREFS; } map->refs++; } match = map; break; } } This code is wrong at a couple different levels, particularly in the case of a fastrpc_mmap_create-->fastrpc_mmap_find call coming from userland such as in the FASTRPC_IOCTL_MEM_MAP ioctl. I think this code path may not be intended to be reachable from userland at all - although even for requests issued from kernel-land, the contract for this code appears to have some correctness issues. This code uses map->va for finding an associated mapping which for these heap addresses comes from a call to dma_alloc_attrs inside of fastrpc_alloc_cma_memory. dma_alloc_attrs has two different modes of operation - one returns a kernel virtual address to the allocated memory, and the other returns a struct page pointer that serves as an opaque cookie for the allocated memory. We have the latter case for this invocation of dma_alloc_attrs because of the DMA_ATTR_NO_KERNEL_MAPPING flag applied in fastrpc_mmap_create_remote_heap. We can see this looking at the debugfs-visible global file in the adsprpc directory:=================================== GMAPS ==================================== fd |phys |size |va -------------------------------------------------------------------------------- -1 |0xE883A000 |0x1000 |0xFFFFFFFE01A20E80 -1 |0xE8839000 |0x1000 |0xFFFFFFFE01A20E40 -1 |0xE8838000 |0x1000 |0xFFFFFFFE01A20E00 -1 |0xE8837000 |0x1000 |0xFFFFFFFE01A20DC0 -1 |0xE8836000 |0x1000 |0xFFFFFFFE01A20D80 -1 |0xE8835000 |0x1000 |0xFFFFFFFE01A20D40 0 |0xE8834000 |0x1000 |0xFFFFFFFE01A20D00 0 |0xE8833000 |0x1000 |0xFFFFFFFE01A20CC0 0 |0xE8832000 |0x1000 |0xFFFFFFFE01A20C80 -1 |0xE8900000 |0x200000 |0xFFFFFFFE01A24000 This means we end up comparing a userland supplied value against a kernel page pointer - behavior of the kernel ioctl FASTRPC_IOCTL_MEM_MAP differs in userland visible ways based on the outcome of the comparison, meaning that userland can leak kernel page pointer addresses by "guessing" a possible address and observing the resulting error code. Here is the output from the attached PoC on a Samsung S23: dm1q:/data/local/tmp $ ./poc Detected address 0xfffffffe01c00000 Final address: 0xfffffffe01a24000 Additionally, because map->va is a struct page pointer as opposed to a genuine address to the underlying buffer, the usage of map->va + map->len is incorrect, and can lead to there being multiple map matches for the same calling parameters. **This bug is subject to a 90-day disclosure deadline. If a fix for this** **issue is made available to users before the end of the 90-day deadline,** **this bug report will become public 30 days after the fix was made** **available. Otherwise, this bug report will become public at the deadline.** The scheduled deadline is 2024-09-22. **For more details, see the Project Zero vulnerability disclosure policy:** **https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-** **policy.html** Related CVE Number: CVE-2024-33060.