QEMU NVMe DMA reentrancy use-after-free¶
QEMU'nun NVMe controller'ında bir use-after-free (CVE-2021-3929): guest bir device DMA'sını controller'ın kendi MMIO BAR'ına yöneltir, recursive olarak
nvme_ctrl_reset()'i tetikler ve in-flight DMA'nın hâlâ kullandığı queue structure'larını free ettirir.
Mechanism¶
Note
QEMU'nun NVMe model'i, guest-directed DMA'yı dma_buf_read() /
dma_buf_write() çağırarak (nvme_tx() / nvme_map_addr() aracılığıyla)
yapar; bunlar address_space_rw()'ye iner. Isolation invariant'ı, bir DMA
destination'ının sıradan RAM olmasıdır; dolayısıyla ona yazmanın device'ın
kendisinde hiçbir yan etkisi yoktur. NVMe bunu kırar: hiçbir şey DMA
target'ının controller'ın kendi MMIO BAR0'ı ile overlap etmediğini
kontrol etmiyordu. Guest bir DMA'yı BAR0'a yönelterek DMA write'ının bir
device register'ına — örn. Controller Configuration register'ına — düşmesini
sağlar; bu da MMIO write path'ini re-enter edip nvme_ctrl_reset()'i
invoke eder. Reset nvme_free_sq()'yu çağırır ve submission/completion-queue
structure'larını orijinal DMA/command processing hâlâ stack üzerindeyken
free eder; dolayısıyla kontrol geri döndüğünde nvme_process_sq() free
edilmiş memory'yi dereference eder — reentrancy-induced bir UAF.
Bu, guest→host sınırını aşar çünkü free-edilip-sonra-kullanılan object'ler QEMU host process'inde yaşar: unprivileged bir guest, driver olmadan NVMe BAR'a ulaşabilir ve host allocator'ında controlled bir UAF, host code execution'ın (en azından DoS) ön koşuludur. e1000e/Tulip/virtio reentrancy bug'larıyla aynı DMA-reentrancy class'ıdır.
Kardeş bug'tan farkı
Aynı DMA-reentrancy class'ı ama ayrı bir CVE'dir. Bu giriş NVMe controller'ı
(CVE-2021-3929) ve nvme_ctrl_reset() → nvme_free_sq() ile free edilen
submission/completion queue'larını kapsar; e1000e varyantı
(QEMU e1000e DMA reentrancy use-after-free,
CVE-2023-3019) bunun yerine e1000e_write_packet_to_guest path'inde NIC
descriptor-ring/packet-context state'ini free eder. Farklı subsystem, farklı
CVE, farklı free site — birbirinin duplicate'i değil.
Walkthrough¶
Public, halihazırda patch'lenmiş materyal: QEMU issue #782, ASan trace ve upstream
fix commit'i 736b01642d85be832385. Yalnızca kavramsal reproduction path'i:
- Guest, DMA PRP/SGL destination'ı guest RAM yerine NVMe BAR0 MMIO region'ına işaret eden bir NVMe admin command'i (örn. bir log-page request) kurar.
- Device DMA'sı (
nvme_tx→dma_buf_write→address_space_rw) BAR0'a yazar venvme_mmio_write/nvme_write_bar'ı re-enter eder. Controller Configuration'a yazmak (CC.EN clear)nvme_ctrl_reset()'i tetikler. nvme_ctrl_reset()→nvme_free_sq(), dıştaki, devam eden command processing tarafından hâlâ referans edilen queue structure'larını free eder.- Unwind'de,
nvme_process_sq()artık-dangling olan pointer üzerinden yazar → use-after-free.
Warning
Tarihsel, patch'lenmiş bir issue (~QEMU 7.0'da düzeltildi). Yalnızca kavramsal — hiçbir host heap grooming'i veya weaponize edilmiş escape chain'i sağlanmıyor.
ASan witness (from the public report, abbreviated)
Beklenen gözlem: guest controller'ın kendi MMIO'sunu hedefleyen NVMe DMA gönderdikten sonra QEMU abort/crash (ya da ASan UAF raporu).
Detection¶
- Host tarafı:
nvme_ctrl_reset/nvme_free_sq'daki free ile birliktenvme_process_sq'da QEMU crash / ASan use-after-free. - Reentrancy guard'ları:
mem_reentrancy_guard(sonraki generic mitigation) ile yapılan build'ler, bir DMA sırasında device MMIO re-entry'sini flag'ler/reddeder. - Behavioral: bir guest'in NVMe BAR'ın içine düşen NVMe DMA address'leri programlaması, herhangi bir gerçek driver için anormaldir.
Mitigation¶
- QEMU fix'ini uygula (commit
736b01642d85be832385):nvme_addr_is_iomem()ekle ve target'ı BAR0 içinde olan DMA'yı reddet;nvme_map_addr()'tanNVME_DATA_TRAS_ERRORdöndür, böylece device asla kendi MMIO'suna DMA yapmaz. - Bir device'ın DMA ortasında kendi MMIO handler'ını re-enter etmesini engelleyen
generic DMA-reentrancy mitigation'ını (
mem_reentrancy_guard) benimse. - Defense-in-depth: QEMU'yu deprivilege/sandbox et (seccomp, sVirt); böylece bir host UAF'i tam host kontrolü olmaz.
References¶
- QEMU issue #782, "nvme: DMA reentrancy issue leads to use-after-free (CVE-2021-3929)": https://gitlab.com/qemu-project/qemu/-/issues/782
- Upstream fix commit
736b01642d85be832385: https://gitlab.com/qemu-project/qemu/-/commit/736b01642d85be832385 - QiuhaoLi, "Recursive MMIO VM Escape PoC" (CVE-2021-3929/3947): https://github.com/QiuhaoLi/CVE-2021-3929-3947