Blame SOURCES/0002-vhost-check-all-range-is-mapped-when-translating-GPA.patch

a6040a
From 575ed8c576daebf38494aa3a10ef95ab806ea97a Mon Sep 17 00:00:00 2001
a6040a
From: Maxime Coquelin <maxime.coquelin@redhat.com>
a6040a
Date: Mon, 23 Apr 2018 11:33:39 +0200
a6040a
Subject: [PATCH 02/11] vhost: check all range is mapped when translating GPAs
a6040a
a6040a
There is currently no check done on the length when translating
a6040a
guest addresses into host virtual addresses. Also, there is no
a6040a
guanrantee that the guest addresses range is contiguous in
a6040a
the host virtual address space.
a6040a
a6040a
This patch prepares vhost_iova_to_vva() and its callers to
a6040a
return and check the mapped size. If the mapped size is smaller
a6040a
than the requested size, the caller handle it as an error.
a6040a
a6040a
This issue has been assigned CVE-2018-1059.
a6040a
a6040a
Reported-by: Yongji Xie <xieyongji@baidu.com>
a6040a
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
a6040a
---
a6040a
 lib/librte_vhost/vhost.c      | 39 +++++++++++++++-----------
a6040a
 lib/librte_vhost/vhost.h      |  6 ++--
a6040a
 lib/librte_vhost/virtio_net.c | 64 +++++++++++++++++++++++++++----------------
a6040a
 3 files changed, 67 insertions(+), 42 deletions(-)
a6040a
a6040a
diff --git a/lib/librte_vhost/vhost.c b/lib/librte_vhost/vhost.c
a6040a
index 51ea720..a8ed40b 100644
a6040a
--- a/lib/librte_vhost/vhost.c
a6040a
+++ b/lib/librte_vhost/vhost.c
a6040a
@@ -59,15 +59,15 @@
a6040a
 uint64_t
a6040a
 __vhost_iova_to_vva(struct virtio_net *dev, struct vhost_virtqueue *vq,
a6040a
-		    uint64_t iova, uint64_t size, uint8_t perm)
a6040a
+		    uint64_t iova, uint64_t *size, uint8_t perm)
a6040a
 {
a6040a
 	uint64_t vva, tmp_size;
a6040a
 
a6040a
-	if (unlikely(!size))
a6040a
+	if (unlikely(!*size))
a6040a
 		return 0;
a6040a
 
a6040a
-	tmp_size = size;
a6040a
+	tmp_size = *size;
a6040a
 
a6040a
 	vva = vhost_user_iotlb_cache_find(vq, iova, &tmp_size, perm);
a6040a
-	if (tmp_size == size)
a6040a
+	if (tmp_size == *size)
a6040a
 		return vva;
a6040a
 
a6040a
@@ -159,30 +159,37 @@ struct virtio_net *
a6040a
 vring_translate(struct virtio_net *dev, struct vhost_virtqueue *vq)
a6040a
 {
a6040a
-	uint64_t size;
a6040a
+	uint64_t req_size, size;
a6040a
 
a6040a
 	if (!(dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM)))
a6040a
 		goto out;
a6040a
 
a6040a
-	size = sizeof(struct vring_desc) * vq->size;
a6040a
+	req_size = sizeof(struct vring_desc) * vq->size;
a6040a
+	size = req_size;
a6040a
 	vq->desc = (struct vring_desc *)(uintptr_t)vhost_iova_to_vva(dev, vq,
a6040a
 						vq->ring_addrs.desc_user_addr,
a6040a
-						size, VHOST_ACCESS_RW);
a6040a
-	if (!vq->desc)
a6040a
+						&size, VHOST_ACCESS_RW);
a6040a
+	if (!vq->desc || size != req_size)
a6040a
 		return -1;
a6040a
 
a6040a
-	size = sizeof(struct vring_avail);
a6040a
-	size += sizeof(uint16_t) * vq->size;
a6040a
+	req_size = sizeof(struct vring_avail);
a6040a
+	req_size += sizeof(uint16_t) * vq->size;
a6040a
+	if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
a6040a
+		req_size += sizeof(uint16_t);
a6040a
+	size = req_size;
a6040a
 	vq->avail = (struct vring_avail *)(uintptr_t)vhost_iova_to_vva(dev, vq,
a6040a
 						vq->ring_addrs.avail_user_addr,
a6040a
-						size, VHOST_ACCESS_RW);
a6040a
-	if (!vq->avail)
a6040a
+						&size, VHOST_ACCESS_RW);
a6040a
+	if (!vq->avail || size != req_size)
a6040a
 		return -1;
a6040a
 
a6040a
-	size = sizeof(struct vring_used);
a6040a
-	size += sizeof(struct vring_used_elem) * vq->size;
a6040a
+	req_size = sizeof(struct vring_used);
a6040a
+	req_size += sizeof(struct vring_used_elem) * vq->size;
a6040a
+	if (dev->features & (1ULL << VIRTIO_RING_F_EVENT_IDX))
a6040a
+		req_size += sizeof(uint16_t);
a6040a
+	size = req_size;
a6040a
 	vq->used = (struct vring_used *)(uintptr_t)vhost_iova_to_vva(dev, vq,
a6040a
 						vq->ring_addrs.used_user_addr,
a6040a
-						size, VHOST_ACCESS_RW);
a6040a
-	if (!vq->used)
a6040a
+						&size, VHOST_ACCESS_RW);
a6040a
+	if (!vq->used || size != req_size)
a6040a
 		return -1;
a6040a
 
a6040a
diff --git a/lib/librte_vhost/vhost.h b/lib/librte_vhost/vhost.h
a6040a
index c8f2a81..de300c1 100644
a6040a
--- a/lib/librte_vhost/vhost.h
a6040a
+++ b/lib/librte_vhost/vhost.h
a6040a
@@ -382,5 +382,5 @@ struct virtio_net {
a6040a
 
a6040a
 uint64_t __vhost_iova_to_vva(struct virtio_net *dev, struct vhost_virtqueue *vq,
a6040a
-			uint64_t iova, uint64_t size, uint8_t perm);
a6040a
+			uint64_t iova, uint64_t *len, uint8_t perm);
a6040a
 int vring_translate(struct virtio_net *dev, struct vhost_virtqueue *vq);
a6040a
 void vring_invalidate(struct virtio_net *dev, struct vhost_virtqueue *vq);
a6040a
@@ -388,10 +388,10 @@ uint64_t __vhost_iova_to_vva(struct virtio_net *dev, struct vhost_virtqueue *vq,
a6040a
 static __rte_always_inline uint64_t
a6040a
 vhost_iova_to_vva(struct virtio_net *dev, struct vhost_virtqueue *vq,
a6040a
-			uint64_t iova, uint64_t size, uint8_t perm)
a6040a
+			uint64_t iova, uint64_t *len, uint8_t perm)
a6040a
 {
a6040a
 	if (!(dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM)))
a6040a
 		return rte_vhost_gpa_to_vva(dev->mem, iova);
a6040a
 
a6040a
-	return __vhost_iova_to_vva(dev, vq, iova, size, perm);
a6040a
+	return __vhost_iova_to_vva(dev, vq, iova, len, perm);
a6040a
 }
a6040a
 
a6040a
diff --git a/lib/librte_vhost/virtio_net.c b/lib/librte_vhost/virtio_net.c
a6040a
index cb1d0cf..79bac59 100644
a6040a
--- a/lib/librte_vhost/virtio_net.c
a6040a
+++ b/lib/librte_vhost/virtio_net.c
a6040a
@@ -205,4 +205,5 @@
a6040a
 	uint32_t mbuf_avail, mbuf_offset;
a6040a
 	uint32_t cpy_len;
a6040a
+	uint64_t dlen;
a6040a
 	struct vring_desc *desc;
a6040a
 	uint64_t desc_addr;
a6040a
@@ -214,6 +215,7 @@
a6040a
 
a6040a
 	desc = &descs[desc_idx];
a6040a
+	dlen = desc->len;
a6040a
 	desc_addr = vhost_iova_to_vva(dev, vq, desc->addr,
a6040a
-					desc->len, VHOST_ACCESS_RW);
a6040a
+					&dlen, VHOST_ACCESS_RW);
a6040a
 	/*
a6040a
 	 * Checking of 'desc_addr' placed outside of 'unlikely' macro to avoid
a6040a
@@ -221,5 +223,6 @@
a6040a
 	 * otherwise stores offset on the stack instead of in a register.
a6040a
 	 */
a6040a
-	if (unlikely(desc->len < dev->vhost_hlen) || !desc_addr) {
a6040a
+	if (unlikely(dlen != desc->len || desc->len < dev->vhost_hlen) ||
a6040a
+			!desc_addr) {
a6040a
 		error = -1;
a6040a
 		goto out;
a6040a
@@ -259,8 +262,9 @@
a6040a
 
a6040a
 			desc = &descs[desc->next];
a6040a
+			dlen = desc->len;
a6040a
 			desc_addr = vhost_iova_to_vva(dev, vq, desc->addr,
a6040a
-							desc->len,
a6040a
+							&dlen,
a6040a
 							VHOST_ACCESS_RW);
a6040a
-			if (unlikely(!desc_addr)) {
a6040a
+			if (unlikely(!desc_addr || dlen != desc->len)) {
a6040a
 				error = -1;
a6040a
 				goto out;
a6040a
@@ -376,10 +380,11 @@
a6040a
 
a6040a
 		if (vq->desc[desc_idx].flags & VRING_DESC_F_INDIRECT) {
a6040a
+			uint64_t dlen = vq->desc[desc_idx].len;
a6040a
 			descs = (struct vring_desc *)(uintptr_t)
a6040a
 				vhost_iova_to_vva(dev,
a6040a
 						vq, vq->desc[desc_idx].addr,
a6040a
-						vq->desc[desc_idx].len,
a6040a
-						VHOST_ACCESS_RO);
a6040a
-			if (unlikely(!descs)) {
a6040a
+						&dlen, VHOST_ACCESS_RO);
a6040a
+			if (unlikely(!descs ||
a6040a
+					dlen != vq->desc[desc_idx].len)) {
a6040a
 				count = i;
a6040a
 				break;
a6040a
@@ -439,4 +444,5 @@
a6040a
 	uint32_t vec_id = *vec_idx;
a6040a
 	uint32_t len    = 0;
a6040a
+	uint64_t dlen;
a6040a
 	struct vring_desc *descs = vq->desc;
a6040a
 
a6040a
@@ -444,9 +450,10 @@
a6040a
 
a6040a
 	if (vq->desc[idx].flags & VRING_DESC_F_INDIRECT) {
a6040a
+		dlen = vq->desc[idx].len;
a6040a
 		descs = (struct vring_desc *)(uintptr_t)
a6040a
 			vhost_iova_to_vva(dev, vq, vq->desc[idx].addr,
a6040a
-						vq->desc[idx].len,
a6040a
+						&dlen,
a6040a
 						VHOST_ACCESS_RO);
a6040a
-		if (unlikely(!descs))
a6040a
+		if (unlikely(!descs || dlen != vq->desc[idx].len))
a6040a
 			return -1;
a6040a
 
a6040a
@@ -531,4 +538,5 @@
a6040a
 	uint32_t desc_offset, desc_avail;
a6040a
 	uint32_t cpy_len;
a6040a
+	uint64_t dlen;
a6040a
 	uint64_t hdr_addr, hdr_phys_addr;
a6040a
 	struct rte_mbuf *hdr_mbuf;
a6040a
@@ -542,8 +550,10 @@
a6040a
 	}
a6040a
 
a6040a
+	dlen = buf_vec[vec_idx].buf_len;
a6040a
 	desc_addr = vhost_iova_to_vva(dev, vq, buf_vec[vec_idx].buf_addr,
a6040a
-						buf_vec[vec_idx].buf_len,
a6040a
-						VHOST_ACCESS_RW);
a6040a
-	if (buf_vec[vec_idx].buf_len < dev->vhost_hlen || !desc_addr) {
a6040a
+						&dlen, VHOST_ACCESS_RW);
a6040a
+	if (dlen != buf_vec[vec_idx].buf_len ||
a6040a
+			buf_vec[vec_idx].buf_len < dev->vhost_hlen ||
a6040a
+			!desc_addr) {
a6040a
 		error = -1;
a6040a
 		goto out;
a6040a
@@ -567,10 +577,12 @@
a6040a
 		if (desc_avail == 0) {
a6040a
 			vec_idx++;
a6040a
+			dlen = buf_vec[vec_idx].buf_len;
a6040a
 			desc_addr =
a6040a
 				vhost_iova_to_vva(dev, vq,
a6040a
 					buf_vec[vec_idx].buf_addr,
a6040a
-					buf_vec[vec_idx].buf_len,
a6040a
+					&dlen,
a6040a
 					VHOST_ACCESS_RW);
a6040a
-			if (unlikely(!desc_addr)) {
a6040a
+			if (unlikely(!desc_addr ||
a6040a
+					dlen != buf_vec[vec_idx].buf_len)) {
a6040a
 				error = -1;
a6040a
 				goto out;
a6040a
@@ -912,4 +924,5 @@
a6040a
 	uint32_t mbuf_avail, mbuf_offset;
a6040a
 	uint32_t cpy_len;
a6040a
+	uint64_t dlen;
a6040a
 	struct rte_mbuf *cur = m, *prev = m;
a6040a
 	struct virtio_net_hdr *hdr = NULL;
a6040a
@@ -927,9 +940,10 @@
a6040a
 	}
a6040a
 
a6040a
+	dlen = desc->len;
a6040a
 	desc_addr = vhost_iova_to_vva(dev,
a6040a
 					vq, desc->addr,
a6040a
-					desc->len,
a6040a
+					&dlen,
a6040a
 					VHOST_ACCESS_RO);
a6040a
-	if (unlikely(!desc_addr)) {
a6040a
+	if (unlikely(!desc_addr || dlen != desc->len)) {
a6040a
 		error = -1;
a6040a
 		goto out;
a6040a
@@ -954,9 +968,10 @@
a6040a
 		}
a6040a
 
a6040a
+		dlen = desc->len;
a6040a
 		desc_addr = vhost_iova_to_vva(dev,
a6040a
 							vq, desc->addr,
a6040a
-							desc->len,
a6040a
+							&dlen,
a6040a
 							VHOST_ACCESS_RO);
a6040a
-		if (unlikely(!desc_addr)) {
a6040a
+		if (unlikely(!desc_addr || dlen != desc->len)) {
a6040a
 			error = -1;
a6040a
 			goto out;
a6040a
@@ -1042,9 +1057,9 @@
a6040a
 			}
a6040a
 
a6040a
+			dlen = desc->len;
a6040a
 			desc_addr = vhost_iova_to_vva(dev,
a6040a
 							vq, desc->addr,
a6040a
-							desc->len,
a6040a
-							VHOST_ACCESS_RO);
a6040a
-			if (unlikely(!desc_addr)) {
a6040a
+							&dlen, VHOST_ACCESS_RO);
a6040a
+			if (unlikely(!desc_addr || dlen != desc->len)) {
a6040a
 				error = -1;
a6040a
 				goto out;
a6040a
@@ -1320,4 +1335,5 @@
a6040a
 		struct vring_desc *desc;
a6040a
 		uint16_t sz, idx;
a6040a
+		uint64_t dlen;
a6040a
 		int err;
a6040a
 
a6040a
@@ -1326,10 +1342,12 @@
a6040a
 
a6040a
 		if (vq->desc[desc_indexes[i]].flags & VRING_DESC_F_INDIRECT) {
a6040a
+			dlen = vq->desc[desc_indexes[i]].len;
a6040a
 			desc = (struct vring_desc *)(uintptr_t)
a6040a
 				vhost_iova_to_vva(dev, vq,
a6040a
 						vq->desc[desc_indexes[i]].addr,
a6040a
-						vq->desc[desc_indexes[i]].len,
a6040a
+						&dlen,
a6040a
 						VHOST_ACCESS_RO);
a6040a
-			if (unlikely(!desc))
a6040a
+			if (unlikely(!desc ||
a6040a
+					dlen != vq->desc[desc_indexes[i]].len))
a6040a
 				break;
a6040a
 
a6040a
-- 
a6040a
1.8.3.1
a6040a