Skip to content

QEMU PVRDMA REG_DSRHIGH uninit pointer (CVE-2021-3608)

Guest'in PVRDMA_REG_DSRHIGH register'ına yaptığı bir write, QEMU'yu düzgün şekilde initialize edilmemiş bir ring->pages array'i ile pvrdma_ring_init()'ten geçirir; böylece daha sonra rdma_pci_dma_unmap()'e uninitialized bir pointer geçirilir — undefined behavior ve bir host crash'i.

Mechanism

Note

PVRDMA, Device Shared Region (DSR) aracılığıyla bootstrap olur: guest, DSR address'ini low ve high yarılarını PVRDMA_REG_DSRLOW / PVRDMA_REG_DSRHIGH register'larına yazarak yayınlar; bu da host'un DSR'yi load etmesini ve ring'lerini pvrdma_ring_init() aracılığıyla initialize etmesini tetikler.

Isolation invariant'ı şudur: bir pointer ancak başarıyla initialize edildikten sonra dereference ya da unmap edilmesi güvenlidir. Bug, REG_DSRHIGH / DSR-load path'inde ring->pages array'inin düzgün şekilde initialize edilmemiş bırakılabilmesidir; error/cleanup path'inde o uninitialized pointer ardından rdma_pci_dma_unmap()'e verilir. Bir uninitialized pointer üzerinde işlem yapmak undefined behavior'dır — en azından bir QEMU crash'i (DoS) ve guest'in etkileyebileceği bir değeri unmap/free etmek, düz bir NULL deref'ten daha tehlikeli bir primitive'dir. Doğrudan bir guest register write'ından ulaşıldığı ve host ayrıcalıklarıyla execute olduğu için guest→host sınırını aşar.

Walkthrough

Üst düzey, public Red Hat advisory'sinden ve hw/rdma/vmw/pvrdma_dev_ring.c'deki upstream fix'ten. Yalnızca kavramsal:

  1. Guest, DSR high address'ini PVRDMA_REG_DSRHIGH MMIO register'ı aracılığıyla yazar ve host'u DSR'yi load edip ring'leri initialize etmeye yönlendirir.
  2. Host'ta pvrdma_ring_init() ring'i kurar ama unpatched path'te ring->pages'i düzgün şekilde initialize edilmemiş bırakabilir (örn. bir mapping adımı yarı yolda fail ettiğinde).
  3. Sonraki cleanup ring->pages'i walk eder ve onun entry'leri üzerinde — uninitialized slot dahil — rdma_pci_dma_unmap()'i çağırır.

    /* representative shape — uninitialized ring->pages reaches unmap */
    for (i = 0; i < ring->max_elems; i++)
        rdma_pci_dma_unmap(dev, ring->pages[i], ...);  /* ring->pages[i] uninitialized */
    
  4. Unmap routine'ine geçirilen uninitialized pointer undefined behavior üretir: bir QEMU crash'i (availability etkisi) ya da attacker-influenced bir değer üzerinde operasyonlar.

Upstream fix, ring->pages'i initialize eder (ve failure/cleanup path'ini sıkar); böylece DMA-unmap routine'ine asla uninitialized bir pointer geçirilmez.

Observable effect on an unpatched build

Vulnerable bir build'de crafted DSR setup'ı bir QEMU crash'i ya da AddressSanitizer / MemorySanitizer altında pvrdma_ring_initrdma_pci_dma_unmap path'inde bir use-of-uninitialized-value veya invalid-free raporu verir.

Warning

Tarihsel, düzeltilmiş bir bug (CVE-2021-3608, CVE-2021-3582 ve CVE-2021-3607 — init_dev_ring()'te integer-overflow kaynaklı unchecked malloc, ayrı bir DoS — ile birlikte 2021 PVRDMA cluster'ının parçası). Yalnızca disclose edilen yapı gösteriliyor — hiçbir offset ya da weaponize edilmiş trigger yok.

Detection

  • Bir guest PVRDMA_REG_DSRHIGH write'ından hemen sonra, backtrace'i pvrdma_ring_init ve rdma_pci_dma_unmap içeren QEMU SIGSEGV / invalid-free veya MSan/ASan uninitialized-value raporları.
  • Untrusted bir guest'ten gelen PVRDMA DSR initialization'ı ile korele QEMU domain crash'lerini gösteren host/libvirt log'ları.
  • DSR (re)initialization'ını tekrar tekrar tetikleyen, özellikle error path'lerini vurmak için tasarlanmış kısmen geçerli mapping'lerle yapan guest'ler için audit.

Mitigation

  • ring->pages'i initialize eden upstream fix'i uygula (QEMU ≥ 6.1.0; distro'lar tarafından backport'landı); böylece rdma_pci_dma_unmap()'e hiçbir uninitialized pointer ulaşmaz.
  • PVRDMA device'ını untrusted guest'lere expose etme — RDMA'nın gerekli olmadığı yerlerde device model'inden çıkar.
  • QEMU'yu libvirt altında SELinux/sVirt ve seccomp ile unprivileged çalıştır; hardening ile compile et (örn. CI'da trap-on-uninitialized tooling) ki bu class erken yakalansın.

References