diff --git a/qemu.spec b/qemu.spec index 9ec8e33699c27ab40f0134446683fc34aff1d853..b114aa8c9d1f0fd48a7ace79b0181c96e07bbe35 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1,6 +1,6 @@ Name: qemu Version: 4.1.0 -Release: 57 +Release: 58 Epoch: 2 Summary: QEMU is a generic and open source machine emulator and virtualizer License: GPLv2 and BSD and MIT and CC-BY-SA-4.0 @@ -333,6 +333,7 @@ Patch0320: target-i386-Export-TAA_NO-bit-to-guests.patch Patch0321: usbredir-fix-free-call.patch Patch0322: hw-arm-virt-Init-PMU-for-hotplugged-vCPU.patch Patch0323: uas-add-stream-number-sanity-checks.patch +Patch0324: virtio-net-fix-use-after-unmap-free-for-sg.patch BuildRequires: flex BuildRequires: bison @@ -729,6 +730,9 @@ getent passwd qemu >/dev/null || \ %endif %changelog +* Sun Sep 26 2021 Chen Qun +- virtio-net: fix use after unmap/free for sg + * Wed Sep 15 2021 Chen Qun - uas: add stream number sanity checks. diff --git a/virtio-net-fix-use-after-unmap-free-for-sg.patch b/virtio-net-fix-use-after-unmap-free-for-sg.patch new file mode 100644 index 0000000000000000000000000000000000000000..b1d255b712bb1cd228fba02b8af75c1b20d99041 --- /dev/null +++ b/virtio-net-fix-use-after-unmap-free-for-sg.patch @@ -0,0 +1,123 @@ +From eedfba47e6cf1c544541551c2c9b762c12857205 Mon Sep 17 00:00:00 2001 +From: Jason Wang +Date: Thu, 2 Sep 2021 13:44:12 +0800 +Subject: [PATCH] virtio-net: fix use after unmap/free for sg + +When mergeable buffer is enabled, we try to set the num_buffers after +the virtqueue elem has been unmapped. This will lead several issues, +E.g a use after free when the descriptor has an address which belongs +to the non direct access region. In this case we use bounce buffer +that is allocated during address_space_map() and freed during +address_space_unmap(). + +Fixing this by storing the elems temporarily in an array and delay the +unmap after we set the the num_buffers. + +This addresses CVE-2021-3748. + +Reported-by: Alexander Bulekov +Fixes: fbe78f4f55c6 ("virtio-net support") +Cc: qemu-stable@nongnu.org +Signed-off-by: Jason Wang +Signed-off-by: imxcc +--- + hw/net/virtio-net.c | 39 ++++++++++++++++++++++++++++++++------- + 1 file changed, 32 insertions(+), 7 deletions(-) + +diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c +index 6adb0fe252..22d430c7c1 100644 +--- a/hw/net/virtio-net.c ++++ b/hw/net/virtio-net.c +@@ -1265,10 +1265,13 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, + VirtIONet *n = qemu_get_nic_opaque(nc); + VirtIONetQueue *q = virtio_net_get_subqueue(nc); + VirtIODevice *vdev = VIRTIO_DEVICE(n); ++ VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE]; ++ size_t lens[VIRTQUEUE_MAX_SIZE]; + struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; + struct virtio_net_hdr_mrg_rxbuf mhdr; + unsigned mhdr_cnt = 0; +- size_t offset, i, guest_offset; ++ size_t offset, i, guest_offset, j; ++ ssize_t err; + + if (!virtio_net_can_receive(nc)) { + return -1; +@@ -1291,6 +1294,12 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, + + total = 0; + ++ if (i == VIRTQUEUE_MAX_SIZE) { ++ virtio_error(vdev, "virtio-net unexpected long buffer chain"); ++ err = size; ++ goto err; ++ } ++ + elem = virtqueue_pop(q->rx_vq, sizeof(VirtQueueElement)); + if (!elem) { + if (i) { +@@ -1302,7 +1311,8 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, + n->guest_hdr_len, n->host_hdr_len, + vdev->guest_features); + } +- return -1; ++ err = -1; ++ goto err; + } + + if (elem->in_num < 1) { +@@ -1310,7 +1320,8 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, + "virtio-net receive queue contains no in buffers"); + virtqueue_detach_element(q->rx_vq, elem, 0); + g_free(elem); +- return -1; ++ err = -1; ++ goto err; + } + + sg = elem->in_sg; +@@ -1342,12 +1353,13 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, + if (!n->mergeable_rx_bufs && offset < size) { + virtqueue_unpop(q->rx_vq, elem, total); + g_free(elem); +- return size; ++ err = size; ++ goto err; + } + +- /* signal other side */ +- virtqueue_fill(q->rx_vq, elem, total, i++); +- g_free(elem); ++ elems[i] = elem; ++ lens[i] = total; ++ i++; + } + + if (mhdr_cnt) { +@@ -1357,10 +1369,23 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, + &mhdr.num_buffers, sizeof mhdr.num_buffers); + } + ++ for (j = 0; j < i; j++) { ++ /* signal other side */ ++ virtqueue_fill(q->rx_vq, elems[j], lens[j], j); ++ g_free(elems[j]); ++ } ++ + virtqueue_flush(q->rx_vq, i); + virtio_notify(vdev, q->rx_vq); + + return size; ++ ++err: ++ for (j = 0; j < i; j++) { ++ g_free(elems[j]); ++ } ++ ++ return err; + } + + static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf, +-- +2.27.0 +