CVE-2021-31795: PowerVR GPU kernel driver heap overwrite via PhysmemNewRamBackedPMR¶
PowerVR
pvrsrvkm.kodriver'ınınPhysmemNewRamBackedPMRpath'inde doğrulanmamış, user-controlled bir mapping-table index'i; ayrıcalıksız bir uygulamanın allocate edilmiş bir array'in ötesinde kernel heap belleğine 4-byte değerler yazmasına ve komşu slab object'lerini bozmasına izin verir.
Mechanism¶
Neden çalışır: sabit boyutlu bir kernel array'ine yazılan, attacker'ın sağladığı bir translation index
PowerVR (Imagination Technologies) GPU kernel driver'ı pvrsrvkm.ko, bir
bridge ioctl arayüzü sunar: userspace bir request'i marshal eder, hedef
bir subsystem ve function id belirtir, driver da eşleşen kernel handler'ına
dispatch eder. Ulaşılabilir bir handler, PhysmemNewRamBackedPMR() aracılığıyla
(yine _PMRCreate() üzerinden ulaşılarak) system RAM ile backed bir PMR
(Physical Memory Resource) oluşturur.
Sparse/chunked bir PMR oluşturulduğunda driver, her virtual chunk için onu
hangi physical chunk'ın backing yaptığını kaydeden bir mapping table inşa
eder. Bu table, ui32NumVirtChunks entry için boyutlandırılmış bir kernel
heap allocation'ıdır:
/* conceptual layout */
struct mapping_table {
...
IMG_UINT32 aui32Translation[/* ui32NumVirtChunks */];
};
Driver, bu table'ı kullanıcının sağladığı pui32MappingTable array'inden
kabaca şu şekilde doldurur:
/* ui32Temp comes from the user-controlled pui32MappingTable[] */
psMappingTable->aui32Translation[ui32Temp] = ui32Temp;
Kusur, ui32Temp üzerinde eksik bir bounds check'tir (CWE-787,
out-of-bounds write / CWE-122 heap overflow). Index, caller'ın mapping-table
input'undan alınır ama hiçbir zaman ui32NumVirtChunks'a karşı doğrulanmaz.
Array'den büyük bir index sağlayarak caller, aui32Translation array'inin
sonunu aşan bir 4-byte write'a yol açar; bu da slab'de onu takip eden hangi
kernel heap object varsa onun içine yazar. Yazılan değer index'e eşit olduğu
için (ui32Temp), attacker hem yazılan değeri sınırlı bir aralık içinde hem
de out-of-bounds offset'i kontrol eder.
Bu, PowerVR device node'unu açabilen ayrıcalıksız bir Android uygulamasından
ulaşılabilen, kontrol edilebilir ve tekrarlanabilir bir heap overwrite'tır.
2021-04-24'e kadarki pvrsrvkm.ko build'lerini çalıştıran Alcatel 1S 2019
(UNISOC SC9863A) üzerinde raporlanmıştır. Komşu object'lerin heap overwrite'ı
— örneğin bir struct file refcount'u ya da başka bir driver structure'ı —
use-after-free ve privilege escalation'a doğru pivot noktasıdır.
Walkthrough¶
Bug, PowerVR services bridge'inin arkasında yaşar. Kavramsal akış şöyle:
services bridge üzerinden PhysmemNewRamBackedPMR'a ulaşmak
/* Open the PowerVR services device (name varies by platform). */
int fd = open("/dev/pvr_sync", O_RDWR); /* or the platform pvrsrvkm node */
/* Issue the bridge ioctl that maps to the PMR-create handler. The bridge
request names a subsystem (mm bridge) and a function id; the in/out
buffers carry the PhysmemNewRamBackedPMR parameters, including:
- ui32NumVirtChunks (size of the mapping table)
- pui32MappingTable[] (user-controlled translation indices)
*/
PVRSRV_BRIDGE_PACKAGE pkg = { .ui32BridgeID = MM_BRIDGE,
.ui32FunctionID = PMR_CREATE_FN,
.pvParamIn = &in, .pvParamOut = &out };
ioctl(fd, DRM_IOCTL_PVR_SRVKM_CMD, &pkg);
Out-of-bounds write'ı tetiklemek — declare edilmiş chunk sayısından büyük bir mapping-table index'i ayarla:
in.ui32NumVirtChunks = N; /* aui32Translation[] sized for N entries */
uint32_t table[1];
table[0] = N + OOB; /* index never validated against N */
in.pui32MappingTable = table;
/* In the kernel: psMappingTable->aui32Translation[N + OOB] = N + OOB;
-> 4-byte write OOB past the allocation. */
Vulnerable bir build'de beklenen sonuç: kontrol edilebilir bir out-of-bounds offset'te, değeri index aralığıyla sınırlı bir 4-byte heap write (writeup kabaca 0–1000'lik bir değer penceresi tarif ediyor). Faydalı bir victim object'in mapping-table allocation'ından sonra düşmesi için spray yapmak, attacker'ın örneğin bir refcount field'ını bozmasına ve bir object'in erkenden free edilmesine yol açmasına izin verir — bu da ardından kernel read/write'a escalate edilen bir use-after-free'dir. Bu, diğer PowerVR pinned-memory bug'larıyla aynı end-game'i paylaşır; karşılaştır cve-2021-39815-powervr-gpu-driver-pinned-memory-unpin-use.md ve cve-2022-20122-powervr-gpu-driver-pinned-memory-unpin-free.md.
Düzeltilmiş bir driver'da index, aui32Translation'ı indexlemeden önce
ui32NumVirtChunks'a karşı doğrulanır; böylece out-of-range bir mapping entry,
array'in ötesine yazmak yerine bir error ile reddedilir.
Detection¶
Göstergeler
- Ayrıcalıksız process'lerin PowerVR device node'larını açıp, entry'leri declare edilmiş chunk sayısını aşan mapping table'larla PMR-create bridge ioctl'leri göndermesi.
- PMR/mapping-table allocation'larının yakınında KASAN slab-out-of-bounds raporları ya da PowerVR services modülünden kaynaklanan heap corruption oops'ları.
Mitigation¶
Düzeltme ve hardening
- mapping-table index'ini chunk sayısına karşı doğrulayan, 2021-04-24'ten
sonraki bir PowerVR DDK /
pvrsrvkm.kobuild'ine güncelle; vendor'lar bunu firmware/security update'leri aracılığıyla dağıtır. - GPU device node'larına erişimi SELinux ile kısıtla ki rastgele uygulamalar services bridge'e ulaşamasın.
- Slab hardening (freelist hardening, redzone'lar, test sırasında KASAN), komşu-object overwrite'ını silah haline getirmeyi daha az güvenilir kılar.