Rooting with root cause: finding a variant of a Project Zero bug (CVE-2022-46395)¶
Arm Mali GPU driver'ının imported-user-buffer handling'inde bir page use-after-free; CVE-2022-36449'un (Project Zero issue 2327) bir variant'ı olarak bulundu ve untrusted bir app'ten kernel code execution ile root sağlıyor.
Mechanism¶
Note
User application'lar Mali GPU ile KBASE_IOCTL_MEM_IMPORT kullanarak memory paylaşır;
bu, KBASE_MEM_TYPE_IMPORTED_USER_BUF tipinde bir kbase_va_region oluşturur. Backing
page'ler user space'in mülkiyetinde kalır ve import sırasında pin'lenmez. Bunlar, kaynağa
ihtiyaç duyan bir GPU softjob çalıştığında lazy olarak pin'lenir: kbase_jd_user_buf_map,
kbase_jd_user_buf_pin_pages'i çağırır ve bu da user page'ler üzerinde refcount almak için
get_user_pages kullanır, böylece job ortasında free edilmezler.
Kırılan invariant şu: driver, imported page'ler üzerinde oluşturduğu CPU/GPU mapping'lerinin
page'lerle aynı lifetime'a sahip olduğunu varsayar. CVE-2022-36449'un root-cause analizi,
imported memory'nin yönetilme şeklinde bir sapma ortaya çıkardı — imported page'ler üzerindeki
vmap tabanlı mapping'ler (kbase_vmap_prot), artı sticky-resource map/unmap path'i, gerçek
page lifetime'ından desync olabiliyor. KBASE_IOCTL_STICKY_RESOURCE_MAP /
KBASE_IOCTL_STICKY_RESOURCE_UNMAP ioctl'leri, aynı page'leri eşzamanlı olarak map'leyip yazan
softjob'a karşı düzgün synchronization olmadan mapping usage count'larını ayarlar. Sonuç olarak,
kernel hâlâ onlara canlı bir mapping tutarken backing page'ler free edilebilir: klasik bir
page-level use-after-free.
Bu bir variant, yeniden bildirim değil: CVE-2022-36449 patch'lendi, ama aynı sınıftaki imported-memory lifetime karışıklığı komşu bir code path'te hayatta kaldı. Bunu bulmak, sadece patch'i diff'lemekten değil, orijinal bug'ın neden var olduğunu anlamaktan geldi.
Walkthrough¶
Exploit, page UAF'i bir kernel read/write primitive'ine dönüştürür. Üst düzeyde:
1. Import a user buffer: KBASE_IOCTL_MEM_IMPORT -> KBASE_MEM_TYPE_IMPORTED_USER_BUF
2. Make it "sticky": KBASE_IOCTL_STICKY_RESOURCE_MAP
3. Submit a GPU softjob that pins+vmaps the imported pages
(kbase_jd_user_buf_map / kbase_vmap_prot)
4. Race STICKY_RESOURCE_UNMAP against the softjob to drop the pages
while the kernel mapping is still live -> page UAF
Mapping oluşturma ile unmapping arasındaki race window çok dar. Write-up, unmap'i window içine güvenilir şekilde oturtmak için interrupt-driven scheduling (timerfd) kullanıyor.
Warning
Açıktan etkilenen davranış yalnızca KBASE_REG_SHARE_BOTH flag'i olmadan oluşturulan
imported memory için ortaya çıkar — yani import sırasında pin'lenmeyip bunun yerine
job başına lazy olarak pin'lenen page'ler. Variant'ın tetiklediği path tam olarak budur.
Free edilmiş bir page kernel tarafından yeniden kullanıldığında, exploit onu kernel-controlled
state olarak reclaim eder. Write-up, gpu_mappings field'ı sıfırlanacak şekilde bir
kbase_mem_phy_alloc'ın corrupt edilmesini anlatır; bu da aliased bir region'ın shrink
edilmesini sağlar: backing page'ler kaldırılırken alias mapping'ler hayatta kalır ve free
edilmiş page'lere UAF erişimi yeniden türetilir. Bu page'ler daha sonra arbitrary read/write
kurmak için page table'lar (PGD'ler) ya da vmalloc tabanlı kernel object'leri olarak reclaim
edilir; ardından kernel code execution ve root gelir.
Primitive chain (conceptual)
page UAF (imported pages freed under a live mapping)
-> reclaim freed page as kbase_mem_phy_alloc metadata
-> zero gpu_mappings -> shrink aliased region
-> backing pages removed, alias mappings remain (UAF again, controlled)
-> reclaim freed pages as PGD / vmalloc kernel objects
-> arbitrary kernel read/write -> kernel code execution -> root
Detection¶
- Telemetry on
untrusted_appaccess to the Mali device node combined with bursts ofKBASE_IOCTL_STICKY_RESOURCE_MAP/KBASE_IOCTL_STICKY_RESOURCE_UNMAParound imported user buffers is a reasonable behavioural signal for this bug class. - Tight timerfd-driven racing of the unmap path against softjob submission is anomalous for legitimate GPU clients.
Mitigation¶
- Update to Arm Mali driver r42p0 (publicly released 27 January 2023), which fixes the imported-memory lifetime handling.
- On Android, the fix shipped in the May 2023 security update.
- Reported to Arm 17 November 2022; CVE-2022-46395 assigned 5 December 2022 (CVSS 9.3).
- Affected: Arm Mali GPU driver r40p0 and earlier (tested on Pixel 6, November 2022 patch level).