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.