Attacking the Qualcomm Adreno GPU (Project Zero)¶
Ben Hawkes (Project Zero), Qualcomm kgsl GPU driver'ının
/dev/kgsl-3d0üzerinden herhangi bir sandbox'lı Android uygulamasına privileged bir attack surface açtığını gösterir; bu, scratch-buffer corruption ile kernel PC kontrolüne varır (CVE-2019-10567 / CVE-2020-11179).
Mechanism¶
Note
Qualcomm kgsl driver'ı, CPU ile GPU firmware arasında paylaşılan tek sayfalık bir scratch buffer tutar. Kernel, mevcut ring alanını hesaplamak için bu mapping'den GPU ring-buffer read-pointer'larını (RPTR) kgsl_sharedmem_readl() ile okur. Kritik olarak, scratch buffer GPU tarafından da writable'dır — GPU-erişilebilir memory'ye yazabilen herhangi bir GPU command, dolayısıyla kernel'in güvendiği RPTR değerlerini zehirleyebilir. adreno_get_rptr(), scratch'ten kontrolsüz değeri döndürdüğü için, raw GPU command'ları issue edebilen bir attacker, kernel'in ring'in gerçekte olduğundan çok daha fazla boş alanı olduğuna inanmasını sağlayabilir ve bir ring-buffer overwrite tetikleyebilir.
Kırılan invariant: kernel, scratch buffer'dan okunan RPTR değerlerinin mevcut write alanını hesaplamadan önce gerçek ring-buffer sınırları içinde olduğunu asla doğrulamaz.
CVE-2019-10567 (Guang Gong, Ağustos 2019) bunu başlangıçta açığa çıkardı; Qualcomm'ın patch'i scratch buffer'ın GPU adresini randomize etti ama ona GPU write'larını engelleyemedi. CVE-2020-11179 (Ben Hawkes, Haziran 2020), kök nedeni ele alan incomplete-patch bypass'tır: indirect-branch execution sırasında scratch memory'yi korumak.
Walkthrough¶
Attack surface giriş noktası
$ ls -la /dev/kgsl-3d0
crw-rw-rw- 1 system system ... /dev/kgsl-3d0
# SELinux allows untrusted apps to open it:
# allow untrusted_app gpu_device:chr_file { ioctl open read write map ... };
Herhangi bir APK open("/dev/kgsl-3d0", O_RDWR) çağırabilir ve sonra ek izinler olmadan ioctl'ler issue edebilir.
Step 1 – Shared memory allocate et ve scratch adresini kurtar
// Allocate GPU-accessible memory
struct kgsl_gpumem_alloc alloc = { .size = 4096, .flags = KGSL_MEMFLAGS_GPUREADONLY };
ioctl(fd, IOCTL_KGSL_GPUMEM_ALLOC, &alloc);
// Submit CP_MEM_TO_MEM GPU commands that read from the ring's RPTR location
// to recover or brute-force the randomised scratch GPU VA (~2721 candidates)
RPTR disclosure olmadan attacker, write-back doğru adresi doğrulayana kadar kabaca 2 721 olası scratch konumunu iterate eder (cihazda ortalama ~7.5 dakika).
Step 2 – CP_MEM_WRITE ile RPTR'ı corrupt et
// GPU command packet that writes an attacker-controlled value to scratch+offset
uint32_t pkt[] = {
pm4_type3_packet(CP_MEM_WRITE, 3),
scratch_gpuva + RPTR_OFFSET, // destination: RPTR slot in scratch
0xdeadbeef, // fake RPTR — far behind real WPTR
0x00000000
};
submit_ib(fd, pkt, sizeof(pkt));
Kernel artık ring'in boş alanı olmadığı halde olduğuna inanır ve bir sonraki submission'ın gerçek allocation'ın ötesindeki ring entry'lerini overwrite etmesine izin verir.
Step 3 – Context-switch operasyonlarını yarıştır
Ring-buffer context-switch kodu, protected mode disabled iken çalışır. Exploit, GPU execution'ı CP_WAIT_REG_MEM ile durdurur, RPTR write'ını overwrite'ın meşru bir context-switch packet'ine düşeceği şekilde zamanlar ve onu attacker'ın kontrol ettiği bir indirect branch ile değiştirir.
Step 4 – Keyfi physical memory mapping
Hijack edilen indirect branch sırasında protected mode disabled iken, exploit TTBR0'ı (GPU page-table base) attacker'ın inşa ettiği sahte page table'lara işaret edecek şekilde değiştirir ve keyfi physical adresleri GPU-writable olarak map'ler.
Step 5 – Kernel compromise
# Map kernel .text as GPU-writable, overwrite sys_call_table entry
# Demonstrated on Pixel 3a (kernel 4.9.200, build QQ2A.200501.001.B3)
# Success rate ~20% per attempt (repeatable)
Detection¶
untrusted_appetiketli process'lerden gelen, anormal derecede düşük GPU VA'larını (scratch aralığı) hedefleyenCP_MEM_WRITEiçeren beklenmedik GPU command buffer submission'ları.adreno_get_rptr()call site'larında out-of-bounds RPTR değerleri için kernel audit'i.kgsldriver telemetry'si: ring-buffer overflow sayaçlarının beklenmedik şekilde artması.
Mitigation¶
- CVE-2020-11179 patch'i: Qualcomm, indirect-branch execution aktifken GPU command'larının scratch buffer'a yazmasını engelleyen firmware-tarafı kısıtlamalar ekledi (protected-mode enforcement).
- Mümkün olduğunda
IOCTL_KGSL_GPU_COMMAND'ı privileged SELinux domain'leriyle sınırla. adreno_get_rptr()'daki RPTR değerlerini kullanmadan önce ring-buffer sınırlarına karşı doğrula.