Blame SOURCES/0006-vhost-handle-virtually-non-contiguous-buffers-in-Tx.patch

a6040a
From ee0d896b3c3ba2dbf5a7a2598a2d8dbe242a0aa7 Mon Sep 17 00:00:00 2001
a6040a
From: Maxime Coquelin <maxime.coquelin@redhat.com>
a6040a
Date: Mon, 23 Apr 2018 11:33:43 +0200
a6040a
Subject: [PATCH 06/11] vhost: handle virtually non-contiguous buffers in Tx
a6040a
a6040a
This patch enables the handling of buffers non-contiguous in
a6040a
process virtual address space in the dequeue path.
a6040a
a6040a
When virtio-net header doesn't fit in a single chunck, it is
a6040a
copied into a local variablei before being processed.
a6040a
a6040a
For packet content, the copy length is limited to the chunck
a6040a
size, next chuncks VAs being fetched afterward.
a6040a
a6040a
This issue has been assigned CVE-2018-1059.
a6040a
a6040a
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
a6040a
---
a6040a
 lib/librte_vhost/virtio_net.c | 117 ++++++++++++++++++++++++++++++++++--------
a6040a
 1 file changed, 95 insertions(+), 22 deletions(-)
a6040a
a6040a
diff --git a/lib/librte_vhost/virtio_net.c b/lib/librte_vhost/virtio_net.c
a6040a
index 13252e6..47717a1 100644
a6040a
--- a/lib/librte_vhost/virtio_net.c
a6040a
+++ b/lib/librte_vhost/virtio_net.c
a6040a
@@ -996,10 +996,11 @@
a6040a
 {
a6040a
 	struct vring_desc *desc;
a6040a
-	uint64_t desc_addr;
a6040a
+	uint64_t desc_addr, desc_gaddr;
a6040a
 	uint32_t desc_avail, desc_offset;
a6040a
 	uint32_t mbuf_avail, mbuf_offset;
a6040a
 	uint32_t cpy_len;
a6040a
-	uint64_t dlen;
a6040a
+	uint64_t desc_chunck_len;
a6040a
 	struct rte_mbuf *cur = m, *prev = m;
a6040a
+	struct virtio_net_hdr tmp_hdr;
a6040a
 	struct virtio_net_hdr *hdr = NULL;
a6040a
 	/* A counter to avoid desc dead loop chain */
a6040a
@@ -1016,10 +1017,11 @@
a6040a
 	}
a6040a
 
a6040a
-	dlen = desc->len;
a6040a
+	desc_chunck_len = desc->len;
a6040a
+	desc_gaddr = desc->addr;
a6040a
 	desc_addr = vhost_iova_to_vva(dev,
a6040a
-					vq, desc->addr,
a6040a
-					&dlen,
a6040a
+					vq, desc_gaddr,
a6040a
+					&desc_chunck_len,
a6040a
 					VHOST_ACCESS_RO);
a6040a
-	if (unlikely(!desc_addr || dlen != desc->len)) {
a6040a
+	if (unlikely(!desc_addr)) {
a6040a
 		error = -1;
a6040a
 		goto out;
a6040a
@@ -1027,6 +1029,38 @@
a6040a
 
a6040a
 	if (virtio_net_with_host_offload(dev)) {
a6040a
-		hdr = (struct virtio_net_hdr *)((uintptr_t)desc_addr);
a6040a
-		rte_prefetch0(hdr);
a6040a
+		if (unlikely(desc_chunck_len < sizeof(struct virtio_net_hdr))) {
a6040a
+			uint64_t len = desc_chunck_len;
a6040a
+			uint64_t remain = sizeof(struct virtio_net_hdr);
a6040a
+			uint64_t src = desc_addr;
a6040a
+			uint64_t dst = (uint64_t)(uintptr_t)&tmp_hdr;
a6040a
+			uint64_t guest_addr = desc_gaddr;
a6040a
+
a6040a
+			/*
a6040a
+			 * No luck, the virtio-net header doesn't fit
a6040a
+			 * in a contiguous virtual area.
a6040a
+			 */
a6040a
+			while (remain) {
a6040a
+				len = remain;
a6040a
+				src = vhost_iova_to_vva(dev, vq,
a6040a
+						guest_addr, &len,
a6040a
+						VHOST_ACCESS_RO);
a6040a
+				if (unlikely(!src || !len)) {
a6040a
+					error = -1;
a6040a
+					goto out;
a6040a
+				}
a6040a
+
a6040a
+				rte_memcpy((void *)(uintptr_t)dst,
a6040a
+						   (void *)(uintptr_t)src, len);
a6040a
+
a6040a
+				guest_addr += len;
a6040a
+				remain -= len;
a6040a
+				dst += len;
a6040a
+			}
a6040a
+
a6040a
+			hdr = &tmp_hdr;
a6040a
+		} else {
a6040a
+			hdr = (struct virtio_net_hdr *)((uintptr_t)desc_addr);
a6040a
+			rte_prefetch0(hdr);
a6040a
+		}
a6040a
 	}
a6040a
 
a6040a
@@ -1044,10 +1078,11 @@
a6040a
 		}
a6040a
 
a6040a
-		dlen = desc->len;
a6040a
+		desc_chunck_len = desc->len;
a6040a
+		desc_gaddr = desc->addr;
a6040a
 		desc_addr = vhost_iova_to_vva(dev,
a6040a
-							vq, desc->addr,
a6040a
-							&dlen,
a6040a
+							vq, desc_gaddr,
a6040a
+							&desc_chunck_len,
a6040a
 							VHOST_ACCESS_RO);
a6040a
-		if (unlikely(!desc_addr || dlen != desc->len)) {
a6040a
+		if (unlikely(!desc_addr)) {
a6040a
 			error = -1;
a6040a
 			goto out;
a6040a
@@ -1059,10 +1094,28 @@
a6040a
 	} else {
a6040a
 		desc_avail  = desc->len - dev->vhost_hlen;
a6040a
-		desc_offset = dev->vhost_hlen;
a6040a
+
a6040a
+		if (unlikely(desc_chunck_len < dev->vhost_hlen)) {
a6040a
+			desc_chunck_len = desc_avail;
a6040a
+			desc_gaddr += dev->vhost_hlen;
a6040a
+			desc_addr = vhost_iova_to_vva(dev,
a6040a
+					vq, desc_gaddr,
a6040a
+					&desc_chunck_len,
a6040a
+					VHOST_ACCESS_RO);
a6040a
+			if (unlikely(!desc_addr)) {
a6040a
+				error = -1;
a6040a
+				goto out;
a6040a
+			}
a6040a
+
a6040a
+			desc_offset = 0;
a6040a
+		} else {
a6040a
+			desc_offset = dev->vhost_hlen;
a6040a
+			desc_chunck_len -= dev->vhost_hlen;
a6040a
+		}
a6040a
 	}
a6040a
 
a6040a
 	rte_prefetch0((void *)(uintptr_t)(desc_addr + desc_offset));
a6040a
 
a6040a
-	PRINT_PACKET(dev, (uintptr_t)(desc_addr + desc_offset), desc_avail, 0);
a6040a
+	PRINT_PACKET(dev, (uintptr_t)(desc_addr + desc_offset),
a6040a
+			desc_chunck_len, 0);
a6040a
 
a6040a
 	mbuf_offset = 0;
a6040a
@@ -1071,5 +1124,5 @@
a6040a
 		uint64_t hpa;
a6040a
 
a6040a
-		cpy_len = RTE_MIN(desc_avail, mbuf_avail);
a6040a
+		cpy_len = RTE_MIN(desc_chunck_len, mbuf_avail);
a6040a
 
a6040a
 		/*
a6040a
@@ -1079,5 +1132,5 @@
a6040a
 		 */
a6040a
 		if (unlikely(dev->dequeue_zero_copy && (hpa = gpa_to_hpa(dev,
a6040a
-					desc->addr + desc_offset, cpy_len)))) {
a6040a
+					desc_gaddr + desc_offset, cpy_len)))) {
a6040a
 			cur->data_len = cpy_len;
a6040a
 			cur->data_off = 0;
a6040a
@@ -1094,5 +1147,6 @@
a6040a
 			if (likely(cpy_len > MAX_BATCH_LEN ||
a6040a
 				   copy_nb >= vq->size ||
a6040a
-				   (hdr && cur == m))) {
a6040a
+				   (hdr && cur == m) ||
a6040a
+				   desc->len != desc_chunck_len)) {
a6040a
 				rte_memcpy(rte_pktmbuf_mtod_offset(cur, void *,
a6040a
 								   mbuf_offset),
a6040a
@@ -1115,4 +1169,5 @@
a6040a
 		mbuf_offset += cpy_len;
a6040a
 		desc_avail  -= cpy_len;
a6040a
+		desc_chunck_len -= cpy_len;
a6040a
 		desc_offset += cpy_len;
a6040a
 
a6040a
@@ -1133,9 +1188,11 @@
a6040a
 			}
a6040a
 
a6040a
-			dlen = desc->len;
a6040a
+			desc_chunck_len = desc->len;
a6040a
+			desc_gaddr = desc->addr;
a6040a
 			desc_addr = vhost_iova_to_vva(dev,
a6040a
-							vq, desc->addr,
a6040a
-							&dlen, VHOST_ACCESS_RO);
a6040a
-			if (unlikely(!desc_addr || dlen != desc->len)) {
a6040a
+							vq, desc_gaddr,
a6040a
+							&desc_chunck_len,
a6040a
+							VHOST_ACCESS_RO);
a6040a
+			if (unlikely(!desc_addr)) {
a6040a
 				error = -1;
a6040a
 				goto out;
a6040a
@@ -1147,5 +1204,21 @@
a6040a
 			desc_avail  = desc->len;
a6040a
 
a6040a
-			PRINT_PACKET(dev, (uintptr_t)desc_addr, desc->len, 0);
a6040a
+			PRINT_PACKET(dev, (uintptr_t)desc_addr,
a6040a
+					desc_chunck_len, 0);
a6040a
+		} else if (unlikely(desc_chunck_len == 0)) {
a6040a
+			desc_chunck_len = desc_avail;
a6040a
+			desc_gaddr += desc_offset;
a6040a
+			desc_addr = vhost_iova_to_vva(dev, vq,
a6040a
+					desc_gaddr,
a6040a
+					&desc_chunck_len,
a6040a
+					VHOST_ACCESS_RO);
a6040a
+			if (unlikely(!desc_addr)) {
a6040a
+				error = -1;
a6040a
+				goto out;
a6040a
+			}
a6040a
+			desc_offset = 0;
a6040a
+
a6040a
+			PRINT_PACKET(dev, (uintptr_t)desc_addr,
a6040a
+					desc_chunck_len, 0);
a6040a
 		}
a6040a
 
a6040a
-- 
a6040a
1.8.3.1
a6040a