QEMU MemoryRegionOps NULL deref MMIO (CVE-2020-15469)¶
Bazı emulated
MemoryRegionOps, I/O region'larını read veya write callback'i olmadan register ediyordu; guest'in böyle bir region'a yaptığı MMIO access NULL bir handler'ı dereference ederek QEMU'yu crash'liyor (guest-to-host DoS).
Mechanism¶
Bug class: populate edilmemiş bir callback table üzerinden dispatch
QEMU, guest'in memory-mapped I/O'sunu davranışı bir MemoryRegionOps
structure'ı — esasen onun .read ve .write function pointer'ları —
tarafından tanımlanan MemoryRegion object'leri üzerinden yönlendirir. MMIO
dispatch layer'ı, bir guest access'inde hedef region'ı bulup uygun
ops->read() / ops->write() callback'ini çağırır.
Dispatcher'ın varsaydığı invariant, memory_region_init_io() ile
register edilen herhangi bir region'ın kullanılabilir read ve write
handler'ları sağladığıdır. QEMU 4.2.0'da çeşitli device/host bridge'lerdeki
(PCI host controller'lar, PCIe MSI, VFIO ve diğerleri) birkaç
MemoryRegionOps instance'ı, callback'lerinden biri veya her ikisi de eksik
(NULL) olacak şekilde declare edilmişti. Guest böyle bir region'a denk gelen
bir MMIO read ya da write yaptığında, dispatcher bir NULL function pointer'ı
çağırır (veya bir NULL ops slot'unu dereference eder) ve host process'i
fault'a sokar.
Sınır şu yüzden aşılır: tamamen legal bir guest MMIO access'i, hiç bağlanmamış bir host code path'ine ulaşır ve sıradan device access'ini bir host crash'ine dönüştürür.
Related variant: CVE-2020-27821 (ayrı, bağımsız disclose edilen CVE)
Yakın ama farklı bir bug, memory-region cache'in initialize edilmesi
sırasında ortaya çıkar: bir cache size calculation kusuru, MMIO sırasında
MSI-X table'a karşı bir out-of-bounds write'a yol açabilir (guest-to-host
DoS). Bu, root cause'u (eksik MemoryRegionOps callback'i değil, hatalı cache
size hesabı) ve disclosure'u bu not'un konusu olan CVE-2020-15469'dan ayrı
olan CVE-2020-27821'dir. İkisini karıştırmamak gerekir: aynı emulated-MMIO
alanına dokunsalar da bağımsız flaw'lardır.
Walkthrough¶
Public advisory'ye dayanan kavramsal reproduction.
-
Guest,
MemoryRegionOps'unda.readveya.writehandler'ı eksik olan bir device/region tespit eder (bunlar etkilenen 4.2.0 source'larından enumerate edilebiliyordu). -
Guest, o region'ın BAR/aperture'üne bir MMIO read ya da write gönderir.
Conceptual dispatch (logical fragments, not an exploit)
# Emulator side (paraphrased):
static const MemoryRegionOps some_ops = {
/* .read = ... <-- missing in the vulnerable code */
/* .write = ... <-- or this one missing */
.endianness = DEVICE_NATIVE_ENDIAN,
};
# On a guest MMIO access the dispatcher does, roughly:
mr->ops->write(mr->opaque, addr, val, size); # ops->write == NULL -> fault
- Dispatcher NULL pointer üzerinden çağrı yapar; QEMU SIGSEGV alır ve guest (ve o QEMU üzerindeki tüm co-tenant'lar) hypervisor process'ini kaybeder.
Tek bir device değil, tüm bir class
Fix birden fazla MemoryRegionOps tanımına dokundu. Çıkarılacak ders bir
pattern: tek seferlik bir device bug'ı değil — herhangi bir
memory_region_init_io() registration'ı handler sağlamalı (ya da framework
eksik olanları reject/stub etmeli).
Düzeltme, etkilenen MemoryRegionOps structure'larına eksik read/write callback
metotlarını ekledi; böylece erişilebilir her MMIO region'ı geçerli bir handler'a
sahip oldu (ve dispatch artık check etmeden non-NULL ops varsaymıyor).
Detection¶
Host ve telemetry sinyalleri
- Crash signature: memory dispatch path'i içinde
(
memory_region_dispatch_read/write-> indirect call) faulting instruction'ı NULL / near-NULL bir call target'ta olan, bir guest MMIO access'i ile korele QEMU SIGSEGV'i. - ASAN/debug build'leri:
ops->read/ops->writeinvocation'ında rapor edilen NULL-pointer-call veya NULL read/write; ayrı CVE-2020-27821 (MSI-X cache) varyantında ise MSI-X table region'ına karşı bir out-of-bounds write. - Anomali: bir guest'in host-bridge / MSI-X / VFIO region'larının BAR'larını normal bir driver'ın yapmayacağı şekilde probe/poke etmesi.
- Fleet pattern: MMIO access'inde tekrarlayan single-tenant QEMU crash'leri, handle edilmeyen bir region dispatch bug'ının güçlü bir göstergesidir.
Mitigation¶
Remediation
- Patch: eksik
MemoryRegionOpscallback'lerini ekleyen bir QEMU sürümüne yükselt (upstream'de 5.0.x serisinde düzeltildi; distro backport'ları Debian LTS, Oracle, Ubuntu vb. tarafından dağıtıldı). - Attack surface'i azalt: guest'in yalnızca ihtiyaç duyduğu device'ları
sun; daha az emulated MMIO region, daha az erişilebilir
opstable'ı demektir. - Defense in depth: QEMU'yu
-sandbox on(seccomp) ve SELinux/AppArmorsvirtaltında çalıştır; böylece bir crash izole ve atfedilebilir olur. - Engineering practice: NULL
.read/.writeiçeren herhangi birmemory_region_init_io()'yu review/static analysis'te bir defect olarak gör — unset bırakmak yerine explicit handler'lar sağla (log/ignore edenler bile olsa).
References¶
- Red Hat Bugzilla — CVE-2020-15469 (MMIO ops null pointer dereference)
- Debian LTS DLA-2560-1 — qemu security update
- OSV CVE-2020-15469
- NVD CVE-2020-27821 — related, distinct: memory-region cache MSI-X OOB write
- Related primitive: null-pointer-dereference
- Related: QEMU device emulation MMIO/PIO callback memory corruption
- Related: QEMU floppy FDC NULL pointer deref (CVE-2021-20196)