Skip to content

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 mergeable num_buffers header field'ını VirtQueueElement iov'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:

  1. 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).
  2. Host bir paket alır; virtio_net_receive_rcu() segment'leri doldurur, sonra VirtQueueElement iov'unu unmap eder — ki bu bounce buffer'ı free eder.
  3. 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-header num_buffers write'ında host tarafı ASan heap-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_rcu path'ine ulaşılmayabilir, bu da maruziyeti azaltır; yine de patch'leyin.

References