QEMU Tulip device DMA reentrancy¶
Device'ın kendi MMIO region'ına geri işaret eden, guest tarafından programlanan Tulip (DEC 21143) descriptor/buffer adresleri, QEMU'nun emüle DMA'sının device write handler'ına özyinelemeli olarak re-enter etmesine yol açar ve host'ta stack exhaustion veya memory corruption'a sebep olur.
Mechanism¶
İzolasyon invariant'ı: DMA memory'yi hedeflemeli, device callback'lerini değil
QEMU, bir device'ın bus-master DMA'sını pci_dma_read() / pci_dma_write() (nihayetinde address_space_read/write) ile emüle eder. Örtük invariant şu: bir device'ın DMA'sı guest RAM'e erişir. Programlanan DMA adresinin başka bir MMIO region'a — device'ın kendi register'ları dahil çözümlenmesini yapısal olarak hiçbir şey yasaklamaz. Bu olduğunda, bir device'ın MMIO write handler'ının içinden yapılan bir write, senkron olarak aynı stack üzerinde aynı MMIO write handler'ına geri dispatch edilir.
Tulip NIC, TX/RX descriptor'larını (tulip_desc_read / tulip_desc_write) read/write eder ve frame buffer'larını (tulip_copy_tx_buffers, tulip_copy_rx_bytes) guest tarafından sağlanan descriptor ve buffer adreslerini kullanarak kopyalar, hedefin device'ın kendi MMIO adresi olmadığını kontrol etmeden. NIC'in BAR base'ini (örn. 0xfebf1000) ayarlayıp ardından bir descriptor'ın buffer pointer'ını o MMIO base'e eşitleyen bir guest, guest tarafından başlatılan tek bir register write'ını sınırsız özyinelemeye çevirir. Bu, guest→host izolasyon sınırını aşar çünkü özyineleme (ve ortaya çıkan herhangi bir overflow) guest'te değil, host QEMU process'inde çalışır. QEMU'nun sonradan benimsediği genel savunma invariant'ı, MemoryRegion başına mem_reentrancy_guard.engaged_in_io flag'idir: hâlihazırda çalışan bir device callback'i, kendi DMA'sı tarafından re-enter edilmemelidir.
Walkthrough¶
Tulip'e özgü DMA-reentrancy stack overflow'u CVE-2022-2962 olarak izlenir (GitLab issue #1171, fix commit 36a894ae "net: tulip: Restrict DMA engine to memories"). Bu, aynı genel sınıfa ait olan CVE-2021-3416 (NIC loopback-mode infinite loop, qemu_receive_packet fix'iyle giderildi) ile karıştırılmamalıdır; loopback CVE'si Tulip descriptor self-MMIO re-entry'sinden ayrı bir alt-bug'dır. Aşağıdaki reproduction path kavramsaldır ve public issue'ya dayanır.
-
Guest'e bir Tulip NIC ekle:
-device tulip,netdev=n0. MMIO BAR base'ini guest PCI config'inden not al (public rapor0xfebf1000kullanır). -
Guest driver'ından, buffer/next pointer'ı guest RAM yerine device'ın kendi MMIO base adresine çözümlenen bir TX descriptor kur.
-
Transmit-enable bit'ini command/status register'ına (CSR6) yazarak transmission'ı başlat. Bu,
tulip_xmit_list_update()→tulip_desc_read()/tulip_copy_tx_buffers()çağırantulip_write()'a girer. -
Descriptor/buffer DMA'sı artık MMIO'ya yazar, bu da
tulip_write()'a yeniden dispatch eder — aynı handler'a aynı host stack'inde re-enter eder.Kavramsal re-entry chain'i (public stack trace'ten)
Her döngü başka bir frame seti push eder; host QEMU process'i sonunda stack overflow'da SIGSEGV ile fault verir. -
Host'taki gözlemlenebilir sonuç: QEMU process'i abort eder (DoS). Yol boyunca ara state bozulduğu için, bu sınıfın daha genel NIC-loopback varyantları da potansiyel olarak memory-corrupting şeklinde işaretlendi.
Tarihî / patch'li
Bu, yalnızca yetkili araştırma, CTF ve defensive testing için açıklanmış ve düzeltilmiş materyaldir. Güvenilmeyen guest'leri patch'siz QEMU'ya karşı çalıştırmayın. Yukarıdaki örnekleyici fragment, weaponize edilmiş uçtan uca host code-execution chain'ini değil, re-entry şeklini gösterir.
Detection¶
- Crash signature: Aynı device handler'ının (
tulip_write/tulip_xmit_list_update) birçok kez tekrar ettiğini gösteren bir backtrace'e sahip bir QEMU SIGSEGV, DMA reentrancy stack overflow'unun kanonik parmak izidir. - Runtime guard tripping: patch'li QEMU'da, genel
mem_reentrancy_guard, kendine re-enter eden bir callback'i abort/short-circuit eder;engaged_in_iozaten set olduğunda loglamak, kötü niyetli descriptor loop'larını yüzeye çıkarır. - Fuzzing: descriptor adreslerini device BAR'larıyla alias yapacak şekilde mutate eden structured device fuzzing (qtest/libFuzzer harness'leri, snapshot greybox fuzzing) bu sınıfı hızlıca yeniden üretir. Bkz. ../hypervisor/snapshot-based-greybox-hypervisor-fuzzing.md ve ../hypervisor/hypercall-fuzzing.md.
- Guest telemetry: NIC descriptor base/buffer pointer'larını RAM yerine MMIO-aralığı physical adreslere (BAR pencereleri) yönlendiren bir guest anormaldir ve device model'inden tespit edilebilir.
Mitigation¶
- Patch: Tulip-spesifik fix commit
36a894ae("net: tulip: Restrict DMA engine to memories", CVE-2022-2962), descriptor read/write path'lerineMemTxAttrs { .memory = true }ekleyerek DMA engine'i MMIO'ya re-entry'den men eder. Ayrıca device MMIO callback'lerini self re-entry'ye karşı koruyan,MemoryRegionüzerinde tanıtılan genelmem_reentrancy_guard'ı ve ilgili NIC-loopback reentrancy fix'lerini (CVE-2021-3416 serisi, loopback path'leriniqemu_receive_packet()'a çevirdi) taşıyan bir QEMU sürümüne güncelle. - Defense in depth: device model gerekmediği yerde legacy emüle NIC'ler (Tulip/pcnet/rtl8139) yerine paravirtualized virtio NIC'leri tercih et.
- Confinement: QEMU'yu sandbox'lı çalıştır (seccomp
-sandbox on, adanmış unprivileged uid, SELinux/sVirt), böylece crash olmuş/suistimal edilmiş bir device model, VM'in kendi process'inin ötesine pivot edemez. - Operational: machine type'ında kullanılmayan emüle NIC model'lerini devre dışı bırak; tek bir guest'ten gelen host-process crash'lerini bir stability bug'ı değil, bir security event olarak ele al.
References¶
- QEMU GitLab issue #1171 — tulip DMA reentrancy leads to stack overflow
- QEMU GitLab commit 36a894ae — net: tulip: Restrict DMA engine to memories (CVE-2022-2962 fix)
- CVE-2021-3416 — NVD (ilgili NIC loopback infinite-loop sınıfı; bu Tulip self-MMIO re-entry'sinden ayrı bir bug)
- Red Hat Bugzilla 1932827 — CVE-2021-3416 QEMU net infinite loop in loopback mode
- oss-security: CVE-2021-3416 disclosure