virtio-net: correctly copy vnet header when flushing TX
When HASH_REPORT is negotiated, the guest_hdr_len might be larger than the size of the mergeable rx buffer header. Using virtio_net_hdr_mrg_rxbuf during the header swap might lead a stack overflow in this case. Fixing this by using virtio_net_hdr_v1_hash instead. Reported-by: Xiao Lei <leixiao.nop@zju.edu.cn> Cc: Yuri Benditovich <yuri.benditovich@daynix.com> Cc: qemu-stable@nongnu.org Cc: Mauro Matteo Cascella <mcascell@redhat.com> Fixes: CVE-2023-6693 Fixes: e22f0603fb2f ("virtio-net: reference implementation of hash report") Reviewed-by: Michael Tokarev <mjt@tls.msk.ru> Signed-off-by: Jason Wang <jasowang@redhat.com> (cherry picked from commit 2220e8189fb94068dbad333228659fbac819abb0) Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
This commit is contained in:
parent
06e11b7589
commit
415f21c723
@ -664,6 +664,11 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs,
|
|||||||
|
|
||||||
n->mergeable_rx_bufs = mergeable_rx_bufs;
|
n->mergeable_rx_bufs = mergeable_rx_bufs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: when extending the vnet header, please make sure to
|
||||||
|
* change the vnet header copying logic in virtio_net_flush_tx()
|
||||||
|
* as well.
|
||||||
|
*/
|
||||||
if (version_1) {
|
if (version_1) {
|
||||||
n->guest_hdr_len = hash_report ?
|
n->guest_hdr_len = hash_report ?
|
||||||
sizeof(struct virtio_net_hdr_v1_hash) :
|
sizeof(struct virtio_net_hdr_v1_hash) :
|
||||||
@ -2630,7 +2635,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
|
|||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
unsigned int out_num;
|
unsigned int out_num;
|
||||||
struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
|
struct iovec sg[VIRTQUEUE_MAX_SIZE], sg2[VIRTQUEUE_MAX_SIZE + 1], *out_sg;
|
||||||
struct virtio_net_hdr_mrg_rxbuf mhdr;
|
struct virtio_net_hdr_v1_hash vhdr;
|
||||||
|
|
||||||
elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
|
elem = virtqueue_pop(q->tx_vq, sizeof(VirtQueueElement));
|
||||||
if (!elem) {
|
if (!elem) {
|
||||||
@ -2647,7 +2652,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (n->has_vnet_hdr) {
|
if (n->has_vnet_hdr) {
|
||||||
if (iov_to_buf(out_sg, out_num, 0, &mhdr, n->guest_hdr_len) <
|
if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
|
||||||
n->guest_hdr_len) {
|
n->guest_hdr_len) {
|
||||||
virtio_error(vdev, "virtio-net header incorrect");
|
virtio_error(vdev, "virtio-net header incorrect");
|
||||||
virtqueue_detach_element(q->tx_vq, elem, 0);
|
virtqueue_detach_element(q->tx_vq, elem, 0);
|
||||||
@ -2655,8 +2660,8 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (n->needs_vnet_hdr_swap) {
|
if (n->needs_vnet_hdr_swap) {
|
||||||
virtio_net_hdr_swap(vdev, (void *) &mhdr);
|
virtio_net_hdr_swap(vdev, (void *) &vhdr);
|
||||||
sg2[0].iov_base = &mhdr;
|
sg2[0].iov_base = &vhdr;
|
||||||
sg2[0].iov_len = n->guest_hdr_len;
|
sg2[0].iov_len = n->guest_hdr_len;
|
||||||
out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
|
out_num = iov_copy(&sg2[1], ARRAY_SIZE(sg2) - 1,
|
||||||
out_sg, out_num,
|
out_sg, out_num,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user