QEMU virtio DMA reentrancy double-free (mem_reentrancy_guard bypass)¶
CVE-2024-3446: virtio-gpu, virtio-serial-bus ve virtio-crypto, kendi bottom-half işlemeleri sırasında guest tarafından programlanmış bir DMA write üzerinden yeniden girilebiliyordu;
mem_reentrancy_guardbottom-half (BH) path'ini kapsamadığı için host nesnelerini iki kez free ediyordu.
Mechanism¶
Note
QEMU bir guest physical address'i host process'ine address_space_map() ile
map'ler. Descriptor düz RAM yerine device MMIO'ya işaret ettiğinde, o map (ya
da sonraki bir erişim) hedef device'ın kendi read/write callback'ine yeniden
girer. Bir device'ın işlem ortasında kendi içine recursive olarak yeniden
girmesini önlemek için QEMU, her MemoryRegion başına
mem_reentrancy_guard.engaged_in_io flag'ini ekledi (e1000e ve nvme
DMA-reentrancy UAF'leri tarafından istismar edilen aynı invariant). İzolasyon
invariant'ı şudur: bir device bir request'i işlerken, guest tarafından
tetiklenen bir DMA o device'ın handler'ının çakışan state üzerinde yeniden
çalışmasına neden olmamalıdır. CVE-2024-3446 o guard'daki bir boşluktur.
virtio-gpu, virtio-serial-bus ve virtio-crypto, işi device'ın I/O path'inin
kullandığı guard nesnesine değil, bus/transport'a bağlı düz
qemu_bh_new_guarded() ile oluşturulan bir QEMUBH'a (bottom half) erteliyordu.
Bir guest, bir buffer/VirtQueueElement zaten in-flight iken device'ın BH'ına
yeniden giren bir DMA ayarlayabilirdi, böylece aynı host nesnesi iki kez free
edilirdi — tamamen kötü niyetli bir guest'ten erişilebilir bir heap double-free,
guest→host sınırını geçiyor.
Walkthrough¶
Public, patch'lenmiş materyal, upstream fix serisi "hw/virtio: Protect from more DMA re-entrancy bugs"'tan (Philippe Mathieu-Daudé, QEMU 9.0 için). Yalnızca kavramsal path:
- Guest, buffer adresi bir device MMIO region'a (RAM değil) çözümlenen bir virtqueue descriptor programlar, böylece QEMU'nun DMA erişimi bir device handler'a yeniden girer.
- Hedeflenen virtio device'ı (gpu / serial-bus / crypto) bir request üzerinde
in-flight'tır ve işlemesini bir
QEMUBH'a schedule etmiştir. O BH yalnızca transport seviyesinde guard edildiği için, nested re-entry handler'ı paylaşılan state üzerinde yeniden çalıştırır. - Handler, dış (hâlâ unwind edilmemiş) çağrının da free ettiği bir host nesnesini
(örn. bir request/
VirtQueueElementile ilişkili bir allocation) free eder — QEMU host heap'inde bir double free.
Temsili fix şekli (alıntılanan seriden): bottom half'in bus guard yerine device'ın
reentrancy guard'ını paylaşması için DeviceState * alan yeni bir helper
virtio_bh_new_guarded() tanıtıldı ve üç device qemu_bh_new_guarded()'tan ona
geçirildi:
hw/virtio: Introduce virtio_bh_new_guarded() helper
hw/display/virtio-gpu: Protect from DMA re-entrancy bugs
hw/char/virtio-serial-bus: Protect from DMA re-entrancy bugs
hw/virtio/virtio-crypto: Protect from DMA re-entrancy bugs
Patch'lenmemiş bir build'de gözlemlenebilir etki
AddressSanitizer / glibc malloc hardening altında, crash, guest MMIO-destekli
descriptor'ı gönderdikten sonra etkilenen virtio device'ının BH callback'i içinde
bir double free or corruption / ASan attempting double-free abort olarak
yüzeye çıkar. İyi niyetli bir guest'te driver'lar descriptor'ları hiçbir zaman
device MMIO'ya yönlendirmez, dolayısıyla bu path asla çalıştırılmaz.
Warning
Tarihsel, düzeltilmiş bug class'ı (CVE-2024-3446, QEMU 9.0'da patch'lendi ve distro'lar tarafından backport edildi). Silahlandırılmış offset'ler, heap-grooming reçetesi ya da uçtan uca RCE chain'i verilmemiştir — yalnızca double-free'nin açıklanmış yapısı.
Detection¶
- Host tarafı crash imzaları: bir virtio device bottom-half'inde
(virtio-gpu / virtio-serial-bus / virtio-crypto) handler'ından kaynaklanan glibc
double free or corruption, ya da ASan double-free raporları. - İşi device-scoped
virtio_bh_new_guarded()yerine guard edilmemişqemu_bh_new_guarded()üzerinden schedule eden virtio device'larını denetleyin. - Reentrancy/DMA fuzzing'i (descriptor'lar device MMIO'ya yönlendirilmiş) bu class'ı deployment öncesi yeniden üretir; aynı harness ilgili e1000e / nvme reentrancy bug'larını da bulur.
Mitigation¶
- QEMU 9.0+'a ya da CVE-2024-3446 backport'unu (
virtio_bh_new_guarded()device-scoped guard'ı) taşıyan bir distro build'ine güncelleyin. - QEMU process'indeki bir double-free'nin host kontrolüne dönüşmek yerine sınırlanması için QEMU'yu libvirt altında SELinux/sVirt sandboxing ile unprivileged çalıştırın.
- Bir device gereksiz olduğunda onu guest'e açmayın (virtio attack surface'ini azaltın).