mmap handler undocumented behavior (CVE-2024-44068)¶
Samsung'un
m2m1shot_scaler0Exynos driver'ı, userspace sayfalarını cihazın IOMMU'suna map'liyor ama PFNMAP sayfaları içinget_page()çağrısını atlarken mapping'i asimetrik şekilde tear-down ediyor — unprivileged bir process, canlı bir I/O-virtual mapping'in altından backing sayfalarını free edip free edilmiş fiziksel belleği okuyup yazabiliyor (in-the-wild bir Android 0-day).
Mechanism¶
Why it works
Bir driver'ın mmap/IOMMU-map yolu, userspace sayfalarını alıp cihazın
bunlara DMA yapabilmesini sağlar. Bunu güvenli yapmak bir reference-count
contract'ı gerektirir: cihaz bir fiziksel sayfaya I/O-virtual mapping
tutarken, kernel o sayfayı pin'li tutmalı (get_page() refcount'u artırır) ve
teardown sırasında tam olarak aldığı reference'ları bırakmalıdır (put_page()).
Simetriyi bozarsan bir use-after-free elde edersin: sayfanın refcount'u
sıfıra düşer ve allocator onu geri dönüştürürken IOMMU mapping hâlâ ona işaret
eder.
CVE-2024-44068 tam olarak bu asimetridir; PFNMAP sayfalarının
undocumented ele alınışında gizlidir. PFNMAP bölgeleri, sayfaları sıradan
refcount'lu page-cache/anon sayfalar yerine driver tarafından yönetilen
fiziksel frame'ler (ör. ION buffer'ları) olan VMA'lardır — vm_normal_page()
bunlar için NULL döner. Samsung IOMMU mapping kodu (sysmmu_map_pte içinde)
bunları özel olarak ele aldı:
if (!pfnmap)
get_page(pte_page(*pte)); // refcount taken ONLY for normal pages
else
mk_lv2ent_pfnmap(ent); // PFNMAP path takes NO reference
Teardown yolu exynos_iommu_unmap_userptr() bunu aynalıyordu — put_page()'i
yalnızca PFNMAP olmayan entry'ler için çağırıyordu. Bu, yalnızca "PFNMAP"
olarak gelen her sayfa mapping'in ömrü boyunca PFNMAP kaldığı sürece kendi
içinde tutarlıdır. Bug şu ki, bir saldırgan sayfa tiplerini karıştırıp
teardown ile race yapabilir; böylece driver sayfaların PFNMAP olduğuna inanır
(reference'ı atlar) ama bunlar aslında sıradan, free edilebilir userspace
sayfalardır. I/O-virtual sayfalar böylece map'ledikleri fiziksel sayfalardan
daha uzun yaşar.
Neden LPE için exploit edilebilir: free edilmiş fiziksel frame'ler kernel
page table olarak reclaim edilebilir. Hâlâ canlı olan I/O mapping üzerinden
yazmak, page-table entry'lerini doğrudan düzenler — bir Kernel Space
Mirroring Attack (KSMA) — böylece kernel belleği userspace'e writable map
edilir ve privileged bir context'te kod çalıştırılır (in-the-wild zincir
cameraserver üzerinden pivot etti).
Walkthrough¶
Sadece yetkili test
SMR-Oct-2024 öncesi Android build'lerindeki savunmasız Exynos SoC'ler (9820/9825/980/990/850/W920) için geçerlidir. Yalnızca sahibi olduğun cihazlarda test et. Aşağıdaki detaylar Project Zero'nun root-cause analizini izler; struct/ioctl ayrıntıları sürüme bağlıdır.
1. Driver'ı aç ve iki allocation tipini hazırla. Exploit hem bir PFNMAP allocation'ına (ION bellek) hem de normal anonymous belleğe ihtiyaç duyar; bunlar multi-plane bir image olarak yapılandırılır ki driver onları plane plane map'lesin:
int fd = open("/dev/m2m1shot_scaler0", O_RDWR);
// Configure src/dst as V4L2_PIX_FMT_YUV420M (3 planes: Y, Cb, Cr).
// Plane buffers mix ION (PFNMAP) and ordinary userspace pages.
struct m2m1shot task = { /* width/height/format/planes ... */ };
2. ioctl ile IOMMU mapping'i başlat. M2M1SHOT_IOC_PROCESS
(0xC0C04D00) m2m scaler'ı sürer: userspace plane'lerini cihaz IOMMU'suna
map'ler, bir firmware command verir, ardından I/O sayfalarını tear-down eder.
3. Timing race'ini kazan. Driver mapping'in ortasındayken, ikinci plane'i
map'lemeye başladığını tespit etmek için mincore() kullan, ardından hemen
PFNMAP allocation'ını munmap() et. PFNMAP yolu hiçbir zaman bir get_page()
reference'ı almadığı için, unmap işlemi IOMMU hâlâ map'lese bile backing
sayfalarını serbest bırakır:
// poll residency to learn the driver's progress
unsigned char vec;
while (mincore(plane2, PAGE_SIZE, &vec) == 0 && !(vec & 1)) { /* spin */ }
munmap(pfnmap_alloc, len); // frees physical pages still mapped in IOMMU
4. Firmware op üzerinden use-after-free. Driver işlemi tamamlar — firmware
command M2M1SHOT_OP_CSC_NARROW veriyi artık stale olan I/O mapping'ler
üzerinden, yani free edilmiş fiziksel sayfalara/sayfalardan kopyalar.
5. Free edilmiş frame'leri page table olarak reclaim et ve escalate et. Spray yap ki free edilmiş fiziksel sayfalar kernel page table olarak yeniden kullanılsın, ardından canlı I/O mapping üzerinden PTE'leri yaz (KSMA) ve arbitrary kernel R/W elde edip privileged bir process'te çalış.
On a KASAN/test build, triggering step 4 prints a use-after-free with the
m2m1shot / exynos-iommu free and use stacks — the ground-truth trigger signal.
Detection¶
- KASAN test kernel'leri, 4. adımda
exynos_iommu_*/m2m1shotstack'leriyle birlikteuse-after-freeraporlar. - Bu açık in the wild exploit edildi; Google TAG/Project Zero (Xingyu Jin,
Clément Lecigene) işaretledi. Production filolarında, karışık ION/anon
multi-plane buffer'larla
M2M1SHOT_IOC_PROCESSveren ve sıkımunmap()race'i yapan unprivileged process'leri izle. - Scaler ioctl'leriyle ilişkili,
exynos-iommuiçindeki beklenmedik IOMMU fault'ları veya panic'ler bir sinyaldir.
Mitigation¶
- Patch: Samsung SMR-Oct-2024, reference-counting asimetrisini düzeltir; böylece PFNMAP ve normal sayfalar hem map hem unmap'te tutarlı şekilde ele alınır.
- Platform izin veriyorsa,
/dev/m2m1shot_scaler0erişimini kısıtla ki media-scaler driver'ı keyfi app'ler tarafından erişilebilir olmasın. - Generic hardening (freelist randomization, page-table protections, KASLR) reclaim/KSMA finisher'ının maliyetini artırır ama temeldeki refcount bug'ını düzeltmez.