QEMU virtio-net descriptor use-after-free (num_buffers after unmap)¶
CVE-2021-3748:
virtio_net_receive_rcu()içinde, bir guest bir RX buffer'ını non-direct (bounce-buffered) bir region'a yerleştirdiğinde, QEMU mergeablenum_buffersheader field'ınıVirtQueueElementiov'u zaten unmap edilmiş ve bounce buffer'ı free edilmiş olduktan sonra set ediyordu — host tarafı bir use-after-free.
Mechanism¶
Note
Zero-copy receive için QEMU her guest RX descriptor'ını address_space_map() ile
kendi address space'ine map'ler ve gelen paketi doğrudan guest belleğine yazar.
Descriptor'ın hedefi doğrudan map'lenemez olduğunda (bir MMIO / non-RAM
"non-direct access region"), memory core gerçek bir RAM pointer'ı geri veremez, bu
yüzden bir bounce buffer allocate eder; address_space_unmap()'te o bounce
buffer guest'e geri flush edilir ve sonra free edilir. İzolasyon invariant'ı
şudur: host, bir iov'u unmap etmeden önce o map'lenmiş iov'a tüm write'ları
bitirmelidir. Mergeable RX buffer'larla virtio-net, num_buffers field'ı ancak
tüm segment'ler tüketildiğinde bilinen bir virtio_net_hdr_mrg_rxbuf yazar — ama
receive loop'u element'i unmap etti (bounce buffer'ı free ederek) ve sonra
num_buffers'ı artık free edilmiş buffer'a yazdı. Bir RX descriptor'ı kasıtlı
olarak non-direct bir region'a yönlendiren bir guest bunu host heap'te bir
use-after-free'ye dönüştürür; guest'ten kontrol edilebilir ve dolayısıyla bir
guest→host corruption primitive'idir.
Walkthrough¶
Public, patch'lenmiş materyal, upstream commit
bedd7e93d01961fcb16a97ae45d93acf357e11f6'tan (Jason Wang, "virtio-net: fix
use-after-free of sg"). Yalnızca kavramsal path:
- Guest
VIRTIO_NET_F_MRG_RXBUF'u (mergeable RX buffer'lar) etkinleştirir ve buffer adresi bir non-direct access region'da yatan bir RX descriptor gönderir (böylece memory core erişimi bounce-buffer etmek zorunda kalır). - Host bir paket alır;
virtio_net_receive_rcu()segment'leri doldurur, sonraVirtQueueElementiov'unu unmap eder — ki bu bounce buffer'ı free eder. - Kod ardından
mhdr.num_buffers'ı o free edilmiş buffer'a bir pointer üzerinden geri yazar → use-after-free.
Temsili fix şekli (alıntılanan commit'ten): her element doldurulurken unmap/free etmek
yerine, element'ler ve uzunlukları stash edilir ve free'ler num_buffers yazılana
kadar ertelenir:
- virtqueue_fill(q->rx_vq, elem, total, i++);
- g_free(elem);
+ elems[i] = elem; /* VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE] */
+ lens[i] = total; /* size_t lens[VIRTQUEUE_MAX_SIZE] */
...
+ /* write num_buffers first, THEN: */
+ for (j = 0; j < i; j++) { virtqueue_fill(...elems[j], lens[j]...); g_free(elems[j]); }
Patch'lenmemiş bir build'de gözlemlenebilir etki
ASan altında, mergeable buffer'larla non-direct bir descriptor'a yapılan RX,
virtio_net_receive_rcu içinde num_buffers store'unda bir heap-use-after-free
WRITE verir; glibc'de heap corruption / QEMU abort olarak kendini gösterir. Normal
guest NIC driver'ları RX buffer'larını her zaman düz RAM'e yerleştirir, dolayısıyla
bu path iyi niyetli şekilde asla isabet almaz.
Warning
Tarihsel, düzeltilmiş (CVE-2021-3748, QEMU 6.2.0'da düzeltildi). Yalnızca açıklanmış root cause seviyesinde belgelenmiştir — exploit offset'leri, heap grooming ya da silahlandırılmış escape chain yok.
Detection¶
virtio_net_receive_rcu(hw/net/virtio-net.c) içinde, özellikle mergeable-headernum_bufferswrite'ında host tarafı ASanheap-use-after-free; ya da QEMU'nun network RX path'inde glibc heap-corruption abort'ları.- non-RAM / MMIO region'larına işaret eden guest RX descriptor'larını işaretleyin (receive path'inde bounce-buffer kullanımı anormaldir).
- non-direct buffer adresleriyle yapılan Network-RX descriptor fuzzing'i bu class'ı yeniden üretir.
Mitigation¶
- QEMU 6.2.0+'a ya da CVE-2021-3748 backport'una (commit
bedd7e93…) sahip bir distro build'ine güncelleyin. - QEMU process'indeki bir UAF'in sınırlanması için QEMU'yu SELinux/sVirt sandboxing ile unprivileged çalıştırın.
- vhost-net / hardware offload kullanımdayken emulate edilmiş
virtio_net_receive_rcupath'ine ulaşılmayabilir, bu da maruziyeti azaltır; yine de patch'leyin.
References¶
- Red Hat Bugzilla 1998514 — CVE-2021-3748 virtio-net heap use-after-free in virtio_net_receive_rcu
- Upstream fix commit bedd7e93d01961fcb16a97ae45d93acf357e11f6
- NVD — CVE-2021-3748
- QEMU commit abe300d9 — follow-up error-path fix referencing CVE-2021-3748
- Related: paravirtualized device descriptor confusion, address_space_map DMA reentrancy, virtio-net error-path memory leak (CVE-2022-26353, follow-up regression)