QEMU PVRDMA CREATE_MR mremap flaw (CVE-2021-3582)¶
PVRDMA_CMD_CREATE_MRgönderen kötü niyetli bir guest, QEMU'nunpvrdma_map_to_pdir()'ini başlangıçta allocate edilenden daha fazla page'imremap()etmeye sürer; yeni mapping'i host buffer'ının ötesine yürütür ve host QEMU process'ini corrupt eder/crash'ler.
Mechanism¶
Note
PVRDMA (hw/rdma/vmw/pvrdma), QEMU'nun paravirtual RDMA device'ıdır. Bir
memory region register etmek için guest bir PVRDMA_CMD_CREATE_MR command'i
submit eder; host create_mr() path'i nihayetinde, guest tarafından sağlanan
page directory / page table list'inden tek bir contiguous host mapping'i
monte etmek için pvrdma_map_to_pdir()'i çağırır. Bunu, guest-controlled bir
chunk count'u (nchunks) kullanarak, ardışık guest DMA chunk'larını iteration
başına bir tane olacak şekilde birlikte mremap() ederek yapar.
Isolation invariant'ı şudur: device'ın birbirine dikiş attığı chunk sayısı
asla length / TARGET_PAGE_SIZE'ı aşmamalı — yani destination mapping'in
başlangıçta rezerve edildiği boyutu. Bug, loop'un guest'in nchunks'ına
güvenmesi ve her yeni mapping'in önceden rezerve edilmiş region'ın içinde
kalıp kalmadığını kontrol etmeden remap etmeye devam etmesidir. nchunks,
region'ın tutabileceğinden büyük olduğunda, tekrarlanan mremap() working
pointer'ı başlangıçta allocate edilmiş host memory sınırının ötesine
uzatır. Tüm bunlar host ayrıcalıklarıyla host QEMU process'inde çalıştığı
için, bir out-of-bounds host access'i (DoS ve prensipte host memory
corruption'ı) doğrudan bir guest command'inden ulaşılır — bir guest→host sınır
aşımı.
Walkthrough¶
Üst düzey, public Red Hat advisory'sinden ve hw/rdma/vmw/pvrdma_cmd.c'ye yapılan
upstream fix'ten. Yalnızca kavramsal:
- Guest driver bir memory-region registration'ı inşa eder: buffer'ı tanımlayan
bir page directory ve page table'ları yerleştirir ve belirtilen region
length'i ile tutarsız bir chunk count set eder
(
nchunks > length / TARGET_PAGE_SIZE). - Guest
PVRDMA_CMD_CREATE_MRsubmit eder. Host'ta, command dispatchercreate_mr()'a yönlendirir; bu da chunk'ları coalesce etmek içinpvrdma_map_to_pdir()'i çağırır. -
pvrdma_map_to_pdir(), tek bir contiguous mapping'i büyütmek içinmremap()çağırarak chunk'lar üzerinde loop yapar — ama yeni mapping'inlength'ten rezerve edilen region içinde kalıp kalmadığını verify etmez. -
Iteration count'u
length'in rezerve ettiğini aştığında, mapping target'ı başlangıçta allocate edilmiş sınırın ötesine yürür — QEMU'yu crash'leyen (availability etkisi) ve kavramsal olarak komşu host state'ini corrupt edebilen bir out-of-bounds host operation'ı.
Upstream fix, loop'u bound eder; böylece device asla region length'inin izin
verdiğinden fazlasını remap etmez ve nchunks'a güvenmek yerine malformed
command'i reddeder.
Observable effect on an unpatched build
Vulnerable bir build'de crafted CREATE_MR, bir QEMU crash'i (SIGSEGV) ya da
AddressSanitizer altında mremap()/page-coalescing path'inde
pvrdma_map_to_pdir()'de köklenen bir out-of-bounds raporu üretir.
Warning
Tarihsel, düzeltilmiş bir bug (CVE-2021-3582, CVE-2021-3607 ve CVE-2021-3608'i de içeren 2021 PVRDMA cluster'ının parçası). Yalnızca disclose edilen yapı gösteriliyor — hiçbir offset, heap layout'u ya da uçtan uca host exploit'i yok.
Detection¶
- Bir guest
PVRDMA_CMD_CREATE_MR'ından kısa süre sonra backtrace'ipvrdma_map_to_pdir/create_mriçeren QEMU SIGSEGV veya ASan heap/OOB raporu. - Untrusted bir guest'ten gelen PVRDMA command trafiği ile korele bir QEMU domain crash'ini gösteren host log'ları / libvirt.
- Chunk count'u declare edilen region length'i ile tutarsız olan memory-region registration'ları submit eden guest'ler için audit/telemetry.
Mitigation¶
pvrdma_map_to_pdir()remap loop'unulength / TARGET_PAGE_SIZE'a karşı bound eden upstream fix'i uygula (QEMU ≥ 6.1.0 serisi; distro'lar tarafından backport'landı).- PVRDMA device'ını untrusted guest'lere expose etme; PVRDMA nadiren gereklidir ve büyük bir guest→host attack surface'idir — mümkün olan yerde guest'in device model'inden çıkar.
- QEMU'yu libvirt altında SELinux/sVirt ve seccomp ile unprivileged çalıştır; böylece bir OOB access host'a değil, sandbox'lanmış process'e sınırlı kalır.