Skip to content

QEMU NVMe out-of-bounds arbitrary memory read (unvalidated guest offset)

QEMU'nun NVMe nvme_changed_nslist() Get-Log-Page handler'ında bir out-of-bounds read (CVE-2021-3947): guest-controlled bir log-page offset'i buffer'a karşı validate edilmiyor ve komşu host memory'sini guest'e geri sızdırıyor.

Mechanism

Note

NVMe Get Log Page, guest'in controller log'larını bir guest buffer'ına okumasına izin verir ve log içine guest tarafından sağlanan bir offset (off) ile bir length destekler. QEMU'nun Changed-Namespace-List handler'ı hw/nvme/ctrl.c içindeki nvme_changed_nslist(), sabit boyutlu bir log buffer'ından o offset'te kopyalar. Isolation invariant'ı, off'un log buffer'ı içinde kalması ve böylece device'ın yalnızca kendi log byte'larını döndürmesidir. Bug: off (ve kısmen guest-controlled length) off, buffer size'ına karşı validate edilmeden index/copy için kullanılır — buffer'dan büyük bir off underflow/over-index yapar, dolayısıyla device hedeflenen buffer'ın ötesini okur ve komşu QEMU-process memory'sini guest'e döndürür.

Bu, host→guest sınırını bir information-disclosure primitive olarak aşar: NVMe access'i olan kötü niyetli bir guest, asla görmemesi gereken host process memory'sini (heap içeriği, ASLR'yi yenmek için kullanılabilir pointer'lar) okur; bu, tipik bir escape chain'in leak yarısıdır. Yayınlanmış PoC'lerde NVMe reentrancy UAF'i (CVE-2021-3929) ile doğal olarak eşleşir.

Walkthrough

Public, halihazırda patch'lenmiş materyal: Red Hat Bugzilla 2021869 ve upstream fix commit'i e2c57529c9306e4 (~QEMU 6.2'de düzeltildi). Yalnızca kavramsal reproduction path'i:

  1. Guest, Changed Namespace List için bir Get Log Page admin command'i gönderir; kendi kontrolündeki bir log-page offset off'unu (ve length'i) seçer.
  2. nvme_changed_nslist(), copy'yi sabit log buffer'ı içinde konumlandırmak için off'u kullanır ama off/length'i buffer sınırlarına karşı kontrol etmez.
  3. off buffer'ın ötesine ayarlandığında (örn. > 4096), copy byte'larını log buffer'ına komşu host memory'sinden kaynaklar.
  4. Bu out-of-bounds byte'lar command'in DMA result'unda döndürülür → guest sızdırılmış host memory'sini okur.

Warning

Tarihsel, patch'lenmiş bir issue. Yalnızca kavramsal; hiçbir offset ya da weaponize edilmiş leak chain'i yok.

Representative fix shape (illustrative)
/* fixed: validate guest offset before using it */
if (off >= sizeof(nslist)) {
    return NVME_INVALID_FIELD | NVME_DNR;   /* reject OOB offset */
}
trans_len = MIN(sizeof(nslist) - off, buf_len);
return nvme_c2h(n, ((uint8_t *)nslist) + off, trans_len, req);

Beklenen gözlem: bir guest'in büyük bir offset ile bir Changed-Namespace log page'inden non-zero/garbage byte'lar okuması; ASan altında nvme_changed_nslist'te bir heap-buffer-overflow READ.

Detection

  • Host tarafı: ASan/instrumented build'ler nvme_changed_nslist'te bir heap OOB read rapor eder.
  • Behavioral: bir guest'in log size'ının ötesindeki offset'lerle Get-Log-Page göndermesi anormaldir; out-of-range off içeren NVMe admin command'lerini logla/audit et.
  • Guest'te yüzeye çıkan sızdırılmış içerik (non-log byte'lar) gözlemlenebilir sinyaldir.

Mitigation

  • CVE-2021-3947 için QEMU fix'ini uygula (commit e2c57529c9306e4): kopyalamadan önce guest tarafından sağlanan log offset/length'ini buffer'a karşı validate et.
  • NVMe emulation'ını patch'li tut ve ihtiyaç duymayan guest'ler için emule edilen NVMe device'ını devre dışı bırakarak attack surface'i daralt.
  • Defense-in-depth: bir info-leak'in ulaşabileceği host memory'sini sınırlamak için QEMU'yu deprivilege/sandbox et (seccomp, sVirt).

References

  • Red Hat Bugzilla 2021869 (CVE-2021-3947, QEMU NVMe out-of-bounds memory read): https://bugzilla.redhat.com/show_bug.cgi?id=2021869
  • QiuhaoLi, "Recursive MMIO VM Escape PoC" (CVE-2021-3929/3947): https://github.com/QiuhaoLi/CVE-2021-3929-3947