Skip to content

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:

  1. 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.
  2. Device DMA'sı (nvme_txdma_buf_writeaddress_space_rw) BAR0'a yazar ve nvme_mmio_write/nvme_write_bar'ı re-enter eder. Controller Configuration'a yazmak (CC.EN clear) nvme_ctrl_reset()'i tetikler.
  3. nvme_ctrl_reset()nvme_free_sq(), dıştaki, devam eden command processing tarafından hâlâ referans edilen queue structure'larını free eder.
  4. 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)
WRITE of size 2 ... thread T0
  #0 nvme_process_sq      hw/nvme/ctrl.c:5549
freed by thread T0 here:
  #1 nvme_free_sq         hw/nvme/ctrl.c:3934
  #2 nvme_ctrl_reset      hw/nvme/ctrl.c:5571

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 birlikte nvme_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()'tan NVME_DATA_TRAS_ERROR dö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