Skip to content

CVE-2021-31795: PowerVR GPU kernel driver heap overwrite via PhysmemNewRamBackedPMR

PowerVR pvrsrvkm.ko driver'ının PhysmemNewRamBackedPMR path'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.ko build'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.

References