Skip to content

mmap handler undocumented behavior (CVE-2024-44068)

Samsung'un m2m1shot_scaler0 Exynos driver'ı, userspace sayfalarını cihazın IOMMU'suna map'liyor ama PFNMAP sayfaları için get_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.

ioctl(fd, M2M1SHOT_IOC_PROCESS, &task);   // begins per-plane sysmmu_map_pte()

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_* / m2m1shot stack'leriyle birlikte use-after-free raporlar.
  • 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_PROCESS veren ve sıkı munmap() race'i yapan unprivileged process'leri izle.
  • Scaler ioctl'leriyle ilişkili, exynos-iommu iç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_scaler0 eriş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.

References