DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] DPDK security advisory: CVE-2019-14818
@ 2019-11-12 15:15 Ferruh Yigit
  2019-11-12 15:18 ` [dpdk-dev] [v16.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: Ferruh Yigit @ 2019-11-12 15:15 UTC (permalink / raw)
  To: dpdk-dev; +Cc: security, security-prerelease

A vulnerability was fixed in DPDK.

Some downstream stakeholders were warned in advance in order to coordinate the
release of fixes and reduce the vulnerability window.

Problem:
A malicious container which has direct access to the vhost-user socket can keep
sending messages which may cause leaking resources until resulting a DOS.

All users of the vhost library are strongly encouraged to upgrade as soon as
possible.

CVE-2019-14818
Bugzilla: https://bugs.dpdk.org/show_bug.cgi?id=363
Severity: Medium
CVSS scores: 6.8



Commits:
main repo
https://git.dpdk.org/dpdk/commit/?id=612e17cf6d7b
https://git.dpdk.org/dpdk/commit/?id=bf472259dde6

19.08.1
https://git.dpdk.org/dpdk-stable/commit/?h=19.08&id=fa674d08985f
https://git.dpdk.org/dpdk-stable/commit/?h=19.08&id=6547dd563ea9

18.11.4 (LTS)
https://git.dpdk.org/dpdk-stable/commit/?h=18.11&id=70583a6b9b1c
https://git.dpdk.org/dpdk-stable/commit/?h=18.11&id=f8898927bb16

17.11.8 (LTS)
https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=3b1b44a1c82a
https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=8a8dbd0ec19e
https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=1f6147d9a01f

16.11.10 (LTS EOL)
https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=5fbb5c2919b6
https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=3863340f93b8
https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=8790f4c3bcd2
https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=1bf11cfb7c7c


Stable Releases download links:
DPDK 19.08.1
http://fast.dpdk.org/rel/dpdk-19.08.1.tar.xz

DPDK 18.11.4 (LTS)
http://fast.dpdk.org/rel/dpdk-18.11.4.tar.xz

DPDK 17.11.8 (LTS)
http://fast.dpdk.org/rel/dpdk-17.11.8.tar.xz

DPDK 16.11.10 (LTS EOL)
http://fast.dpdk.org/rel/dpdk-16.11.10.tar.xz


-- 
DPDK Security Team
http://core.dpdk.org/security/



^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v16.11 PATCH v2 1/4] vhost: validate virtqueue size
  2019-11-12 15:15 [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
@ 2019-11-12 15:18 ` Maxime Coquelin
  2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages Maxime Coquelin
                     ` (2 more replies)
  2019-11-12 15:19 ` [dpdk-dev] [v17.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
                   ` (4 subsequent siblings)
  5 siblings, 3 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:18 UTC (permalink / raw)
  To: dev, stable; +Cc: Stefan Hajnoczi, Maxime Coquelin

From: Stefan Hajnoczi <stefanha@redhat.com>

[ backported from upstream commit eb7c574b21cc92792ea5a1f219ddf6dd3cf3b1e1 ]

Check the virtqueue size constraints so that invalid values don't cause
bugs later on in the code.  For example, sometimes the virtqueue size is
stored as unsigned int and sometimes as uint16_t, so bad things happen
if it is ever larger than 65535.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 618d413fe1..8a01c295e7 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -189,6 +189,17 @@ vhost_user_set_vring_num(struct virtio_net *dev,
 
 	vq->size = state->num;
 
+	/* VIRTIO 1.0, 2.4 Virtqueues says:
+	 *
+	 *   Queue Size value is always a power of 2. The maximum Queue Size
+	 *   value is 32768.
+	 */
+	if ((vq->size & (vq->size - 1)) || vq->size > 32768) {
+		RTE_LOG(ERR, VHOST_CONFIG,
+			"invalid virtqueue size %u\n", vq->size);
+		return -1;
+	}
+
 	if (dev->dequeue_zero_copy) {
 		vq->nr_zmbuf = 0;
 		vq->last_zmbuf_idx = 0;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v16.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages
  2019-11-12 15:18 ` [dpdk-dev] [v16.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
@ 2019-11-12 15:18   ` Maxime Coquelin
  2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 3/4] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
  2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 4/4] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
  2 siblings, 0 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:18 UTC (permalink / raw)
  To: dev, stable; +Cc: Maxime Coquelin, Dr . David Alan Gilbert

As soon as some ancillary data (fds) are received, it is copied
without checking its length.

This patch adds the number of fds received to the message,
which is set in read_vhost_message().

This is preliminary work to support sending fds to Qemu.

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
(cherry picked from commit c00bb88d35fe975ede0ea35bdf4f765a2cece7e8)
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/socket.c     | 22 +++++++++++++++++-----
 lib/librte_vhost/vhost_user.c |  2 +-
 lib/librte_vhost/vhost_user.h |  4 +++-
 3 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/lib/librte_vhost/socket.c b/lib/librte_vhost/socket.c
index 805b2e5b23..4a19280fb3 100644
--- a/lib/librte_vhost/socket.c
+++ b/lib/librte_vhost/socket.c
@@ -101,17 +101,23 @@ static struct vhost_user vhost_user = {
 	.mutex = PTHREAD_MUTEX_INITIALIZER,
 };
 
-/* return bytes# of read on success or negative val on failure. */
+/*
+ * return bytes# of read on success or negative val on failure. Update fdnum
+ * with number of fds read.
+ */
 int
-read_fd_message(int sockfd, char *buf, int buflen, int *fds, int fd_num)
+read_fd_message(int sockfd, char *buf, int buflen, int *fds, int max_fds,
+		int *fd_num)
 {
 	struct iovec iov;
 	struct msghdr msgh;
-	size_t fdsize = fd_num * sizeof(int);
-	char control[CMSG_SPACE(fdsize)];
+	char control[CMSG_SPACE(max_fds * sizeof(int))];
 	struct cmsghdr *cmsg;
+	int got_fds = 0;
 	int ret;
 
+	*fd_num = 0;
+
 	memset(&msgh, 0, sizeof(msgh));
 	iov.iov_base = buf;
 	iov.iov_len  = buflen;
@@ -136,11 +142,17 @@ read_fd_message(int sockfd, char *buf, int buflen, int *fds, int fd_num)
 		cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
 		if ((cmsg->cmsg_level == SOL_SOCKET) &&
 			(cmsg->cmsg_type == SCM_RIGHTS)) {
-			memcpy(fds, CMSG_DATA(cmsg), fdsize);
+			got_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+			*fd_num = got_fds;
+			memcpy(fds, CMSG_DATA(cmsg), got_fds * sizeof(int));
 			break;
 		}
 	}
 
+	/* Clear out unused file descriptors */
+	while (got_fds < max_fds)
+		fds[got_fds++] = -1;
+
 	return ret;
 }
 
diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 8a01c295e7..b8f6a9fba5 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -963,7 +963,7 @@ read_vhost_message(int sockfd, struct VhostUserMsg *msg)
 	int ret;
 
 	ret = read_fd_message(sockfd, (char *)msg, VHOST_USER_HDR_SIZE,
-		msg->fds, VHOST_MEMORY_MAX_NREGIONS);
+		msg->fds, VHOST_MEMORY_MAX_NREGIONS, &msg->fd_num);
 	if (ret <= 0)
 		return ret;
 
diff --git a/lib/librte_vhost/vhost_user.h b/lib/librte_vhost/vhost_user.h
index ba78d32684..ea9d304f82 100644
--- a/lib/librte_vhost/vhost_user.h
+++ b/lib/librte_vhost/vhost_user.h
@@ -110,6 +110,7 @@ typedef struct VhostUserMsg {
 		VhostUserLog    log;
 	} payload;
 	int fds[VHOST_MEMORY_MAX_NREGIONS];
+	int fd_num;
 } __attribute((packed)) VhostUserMsg;
 
 #define VHOST_USER_HDR_SIZE offsetof(VhostUserMsg, payload.u64)
@@ -122,7 +123,8 @@ typedef struct VhostUserMsg {
 int vhost_user_msg_handler(int vid, int fd);
 
 /* socket.c */
-int read_fd_message(int sockfd, char *buf, int buflen, int *fds, int fd_num);
+int read_fd_message(int sockfd, char *buf, int buflen, int *fds, int max_fds,
+		int *fd_num);
 int send_fd_message(int sockfd, char *buf, int buflen, int *fds, int fd_num);
 
 #endif
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v16.11 PATCH v2 3/4] vhost: fix possible denial of service on SET_VRING_NUM
  2019-11-12 15:18 ` [dpdk-dev] [v16.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
  2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages Maxime Coquelin
@ 2019-11-12 15:18   ` Maxime Coquelin
  2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 4/4] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
  2 siblings, 0 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:18 UTC (permalink / raw)
  To: dev, stable; +Cc: Maxime Coquelin, Jason Wang

vhost_user_set_vring_num() performs multiple allocations
without checking whether data were previously allocated.

It may cause a denial of service because of the memory leaks
that happen if a malicious vhost-user master keeps sending
VHOST_USER_SET_VRING_NUM request until the slave runs out
of memory.

This issue has been assigned CVE-2019-14818

Reported-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index b8f6a9fba5..cebc72f78f 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -204,6 +204,8 @@ vhost_user_set_vring_num(struct virtio_net *dev,
 		vq->nr_zmbuf = 0;
 		vq->last_zmbuf_idx = 0;
 		vq->zmbuf_size = vq->size;
+		if (vq->zmbufs)
+			rte_free(vq->zmbufs);
 		vq->zmbufs = rte_zmalloc(NULL, vq->zmbuf_size *
 					 sizeof(struct zcopy_mbuf), 0);
 		if (vq->zmbufs == NULL) {
@@ -213,7 +215,8 @@ vhost_user_set_vring_num(struct virtio_net *dev,
 			dev->dequeue_zero_copy = 0;
 		}
 	}
-
+	if (vq->shadow_used_ring)
+		rte_free(vq->shadow_used_ring);
 	vq->shadow_used_ring = rte_malloc(NULL,
 				vq->size * sizeof(struct vring_used_elem),
 				RTE_CACHE_LINE_SIZE);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v16.11 PATCH v2 4/4] vhost: fix possible denial of service by leaking FDs
  2019-11-12 15:18 ` [dpdk-dev] [v16.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
  2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages Maxime Coquelin
  2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 3/4] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
@ 2019-11-12 15:18   ` Maxime Coquelin
  2 siblings, 0 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:18 UTC (permalink / raw)
  To: dev, stable; +Cc: Maxime Coquelin

A malicious Vhost-user master could send in loop hand-crafted
vhost-user messages containing more file descriptors the
vhost-user slave expects. Doing so causes the application using
the vhost-user library to run out of FDs.

This issue has been assigned CVE-2019-14818

Fixes: 8f972312b8f4 ("vhost: support vhost-user")

Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 87 +++++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index cebc72f78f..a6ab131543 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -75,6 +75,36 @@ static const char *vhost_message_str[VHOST_USER_MAX] = {
 	[VHOST_USER_SEND_RARP]  = "VHOST_USER_SEND_RARP",
 };
 
+static void
+close_msg_fds(struct VhostUserMsg *msg)
+{
+	int i;
+
+	for (i = 0; i < msg->fd_num; i++)
+		close(msg->fds[i]);
+}
+
+/*
+ * Ensure the expected number of FDs is received,
+ * close all FDs and return an error if this is not the case.
+ */
+static int
+validate_msg_fds(struct VhostUserMsg *msg, int expected_fds)
+{
+	if (msg->fd_num == expected_fds)
+		return 0;
+
+	RTE_LOG(ERR, VHOST_CONFIG,
+		" Expect %d FDs for request %s, received %d\n",
+		expected_fds,
+		vhost_message_str[msg->request],
+		msg->fd_num);
+
+	close_msg_fds(msg);
+
+	return -1;
+}
+
 static uint64_t
 get_blk_size(int fd)
 {
@@ -1104,35 +1134,59 @@ vhost_user_msg_handler(int vid, int fd)
 	ret = 0;
 	switch (msg.request) {
 	case VHOST_USER_GET_FEATURES:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		msg.payload.u64 = vhost_user_get_features();
 		msg.size = sizeof(msg.payload.u64);
 		send_vhost_message(fd, &msg);
 		break;
 	case VHOST_USER_SET_FEATURES:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_set_features(dev, msg.payload.u64);
 		break;
 
 	case VHOST_USER_GET_PROTOCOL_FEATURES:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		msg.payload.u64 = VHOST_USER_PROTOCOL_FEATURES;
 		msg.size = sizeof(msg.payload.u64);
 		send_vhost_message(fd, &msg);
 		break;
 	case VHOST_USER_SET_PROTOCOL_FEATURES:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_set_protocol_features(dev, msg.payload.u64);
 		break;
 
 	case VHOST_USER_SET_OWNER:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_set_owner();
 		break;
 	case VHOST_USER_RESET_OWNER:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_reset_owner(dev);
 		break;
 
 	case VHOST_USER_SET_MEM_TABLE:
+		if (validate_msg_fds(&msg, msg.payload.memory.nregions) != 0)
+			return -1;
+
 		vhost_user_set_mem_table(dev, &msg);
 		break;
 
 	case VHOST_USER_SET_LOG_BASE:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		ret = vhost_user_set_log_base(dev, &msg);
 		if (ret)
 			break;
@@ -1144,21 +1198,36 @@ vhost_user_msg_handler(int vid, int fd)
 		send_vhost_message(fd, &msg);
 		break;
 	case VHOST_USER_SET_LOG_FD:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		close(msg.fds[0]);
 		RTE_LOG(INFO, VHOST_CONFIG, "not implemented.\n");
 		break;
 
 	case VHOST_USER_SET_VRING_NUM:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_set_vring_num(dev, &msg.payload.state);
 		break;
 	case VHOST_USER_SET_VRING_ADDR:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_set_vring_addr(&dev, &msg.payload.addr);
 		break;
 	case VHOST_USER_SET_VRING_BASE:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_set_vring_base(dev, &msg.payload.state);
 		break;
 
 	case VHOST_USER_GET_VRING_BASE:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_get_vring_base(dev, &msg.payload.state);
 		if (ret)
 			break;
@@ -1167,28 +1236,46 @@ vhost_user_msg_handler(int vid, int fd)
 		break;
 
 	case VHOST_USER_SET_VRING_KICK:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		ret = vhost_user_set_vring_kick(dev, &msg);
 		break;
 	case VHOST_USER_SET_VRING_CALL:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		vhost_user_set_vring_call(dev, &msg);
 		break;
 
 	case VHOST_USER_SET_VRING_ERR:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		if (!(msg.payload.u64 & VHOST_USER_VRING_NOFD_MASK))
 			close(msg.fds[0]);
 		RTE_LOG(INFO, VHOST_CONFIG, "not implemented\n");
 		break;
 
 	case VHOST_USER_GET_QUEUE_NUM:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		msg.payload.u64 = VHOST_MAX_QUEUE_PAIRS;
 		msg.size = sizeof(msg.payload.u64);
 		send_vhost_message(fd, &msg);
 		break;
 
 	case VHOST_USER_SET_VRING_ENABLE:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_set_vring_enable(dev, &msg.payload.state);
 		break;
 	case VHOST_USER_SEND_RARP:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_send_rarp(dev, &msg);
 		break;
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v17.11 PATCH v2 1/4] vhost: validate virtqueue size
  2019-11-12 15:15 [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
  2019-11-12 15:18 ` [dpdk-dev] [v16.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
@ 2019-11-12 15:19 ` Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages Maxime Coquelin
                     ` (2 more replies)
  2019-11-12 15:19 ` [dpdk-dev] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
                   ` (3 subsequent siblings)
  5 siblings, 3 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:19 UTC (permalink / raw)
  To: dev, stable; +Cc: Stefan Hajnoczi, Maxime Coquelin

From: Stefan Hajnoczi <stefanha@redhat.com>

[ backported from upstream commit eb7c574b21cc92792ea5a1f219ddf6dd3cf3b1e1 ]

Check the virtqueue size constraints so that invalid values don't cause
bugs later on in the code.  For example, sometimes the virtqueue size is
stored as unsigned int and sometimes as uint16_t, so bad things happen
if it is ever larger than 65535.

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index bb39999aa4..93e871c5bb 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -247,6 +247,17 @@ vhost_user_set_vring_num(struct virtio_net *dev,
 
 	vq->size = msg->payload.state.num;
 
+	/* VIRTIO 1.0, 2.4 Virtqueues says:
+	 *
+	 *   Queue Size value is always a power of 2. The maximum Queue Size
+	 *   value is 32768.
+	 */
+	if ((vq->size & (vq->size - 1)) || vq->size > 32768) {
+		RTE_LOG(ERR, VHOST_CONFIG,
+			"invalid virtqueue size %u\n", vq->size);
+		return -1;
+	}
+
 	if (dev->dequeue_zero_copy) {
 		vq->nr_zmbuf = 0;
 		vq->last_zmbuf_idx = 0;
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v17.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages
  2019-11-12 15:19 ` [dpdk-dev] [v17.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
@ 2019-11-12 15:19   ` Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 3/4] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 4/4] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
  2 siblings, 0 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:19 UTC (permalink / raw)
  To: dev, stable; +Cc: Maxime Coquelin, Dr . David Alan Gilbert

As soon as some ancillary data (fds) are received, it is copied
without checking its length.

This patch adds the number of fds received to the message,
which is set in read_vhost_message().

This is preliminary work to support sending fds to Qemu.

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
(cherry picked from commit c00bb88d35fe975ede0ea35bdf4f765a2cece7e8)
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/socket.c     | 22 +++++++++++++++++-----
 lib/librte_vhost/vhost_user.c |  2 +-
 lib/librte_vhost/vhost_user.h |  4 +++-
 3 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/lib/librte_vhost/socket.c b/lib/librte_vhost/socket.c
index 88be697c2f..2fa7ea0e09 100644
--- a/lib/librte_vhost/socket.c
+++ b/lib/librte_vhost/socket.c
@@ -117,17 +117,23 @@ static struct vhost_user vhost_user = {
 	.mutex = PTHREAD_MUTEX_INITIALIZER,
 };
 
-/* return bytes# of read on success or negative val on failure. */
+/*
+ * return bytes# of read on success or negative val on failure. Update fdnum
+ * with number of fds read.
+ */
 int
-read_fd_message(int sockfd, char *buf, int buflen, int *fds, int fd_num)
+read_fd_message(int sockfd, char *buf, int buflen, int *fds, int max_fds,
+		int *fd_num)
 {
 	struct iovec iov;
 	struct msghdr msgh;
-	size_t fdsize = fd_num * sizeof(int);
-	char control[CMSG_SPACE(fdsize)];
+	char control[CMSG_SPACE(max_fds * sizeof(int))];
 	struct cmsghdr *cmsg;
+	int got_fds = 0;
 	int ret;
 
+	*fd_num = 0;
+
 	memset(&msgh, 0, sizeof(msgh));
 	iov.iov_base = buf;
 	iov.iov_len  = buflen;
@@ -152,11 +158,17 @@ read_fd_message(int sockfd, char *buf, int buflen, int *fds, int fd_num)
 		cmsg = CMSG_NXTHDR(&msgh, cmsg)) {
 		if ((cmsg->cmsg_level == SOL_SOCKET) &&
 			(cmsg->cmsg_type == SCM_RIGHTS)) {
-			memcpy(fds, CMSG_DATA(cmsg), fdsize);
+			got_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+			*fd_num = got_fds;
+			memcpy(fds, CMSG_DATA(cmsg), got_fds * sizeof(int));
 			break;
 		}
 	}
 
+	/* Clear out unused file descriptors */
+	while (got_fds < max_fds)
+		fds[got_fds++] = -1;
+
 	return ret;
 }
 
diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 93e871c5bb..9773691097 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -1248,7 +1248,7 @@ read_vhost_message(int sockfd, struct VhostUserMsg *msg)
 	int ret;
 
 	ret = read_fd_message(sockfd, (char *)msg, VHOST_USER_HDR_SIZE,
-		msg->fds, VHOST_MEMORY_MAX_NREGIONS);
+		msg->fds, VHOST_MEMORY_MAX_NREGIONS, &msg->fd_num);
 	if (ret <= 0)
 		return ret;
 
diff --git a/lib/librte_vhost/vhost_user.h b/lib/librte_vhost/vhost_user.h
index 76d9fe2fc5..10b8cc5e1a 100644
--- a/lib/librte_vhost/vhost_user.h
+++ b/lib/librte_vhost/vhost_user.h
@@ -130,6 +130,7 @@ typedef struct VhostUserMsg {
 		struct vhost_iotlb_msg iotlb;
 	} payload;
 	int fds[VHOST_MEMORY_MAX_NREGIONS];
+	int fd_num;
 } __attribute((packed)) VhostUserMsg;
 
 #define VHOST_USER_HDR_SIZE offsetof(VhostUserMsg, payload.u64)
@@ -143,7 +144,8 @@ int vhost_user_msg_handler(int vid, int fd);
 int vhost_user_iotlb_miss(struct virtio_net *dev, uint64_t iova, uint8_t perm);
 
 /* socket.c */
-int read_fd_message(int sockfd, char *buf, int buflen, int *fds, int fd_num);
+int read_fd_message(int sockfd, char *buf, int buflen, int *fds, int max_fds,
+		int *fd_num);
 int send_fd_message(int sockfd, char *buf, int buflen, int *fds, int fd_num);
 
 #endif
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v17.11 PATCH v2 3/4] vhost: fix possible denial of service on SET_VRING_NUM
  2019-11-12 15:19 ` [dpdk-dev] [v17.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages Maxime Coquelin
@ 2019-11-12 15:19   ` Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 4/4] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
  2 siblings, 0 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:19 UTC (permalink / raw)
  To: dev, stable; +Cc: Maxime Coquelin, Jason Wang

vhost_user_set_vring_num() performs multiple allocations
without checking whether data were previously allocated.

It may cause a denial of service because of the memory leaks
that happen if a malicious vhost-user master keeps sending
VHOST_USER_SET_VRING_NUM request until the slave runs out
of memory.

This issue has been assigned CVE-2019-14818

Reported-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 9773691097..781734e9e3 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -262,6 +262,8 @@ vhost_user_set_vring_num(struct virtio_net *dev,
 		vq->nr_zmbuf = 0;
 		vq->last_zmbuf_idx = 0;
 		vq->zmbuf_size = vq->size;
+		if (vq->zmbufs)
+			rte_free(vq->zmbufs);
 		vq->zmbufs = rte_zmalloc(NULL, vq->zmbuf_size *
 					 sizeof(struct zcopy_mbuf), 0);
 		if (vq->zmbufs == NULL) {
@@ -271,7 +273,8 @@ vhost_user_set_vring_num(struct virtio_net *dev,
 			dev->dequeue_zero_copy = 0;
 		}
 	}
-
+	if (vq->shadow_used_ring)
+		rte_free(vq->shadow_used_ring);
 	vq->shadow_used_ring = rte_malloc(NULL,
 				vq->size * sizeof(struct vring_used_elem),
 				RTE_CACHE_LINE_SIZE);
@@ -281,6 +284,8 @@ vhost_user_set_vring_num(struct virtio_net *dev,
 		return -1;
 	}
 
+	if (vq->batch_copy_elems)
+		rte_free(vq->batch_copy_elems);
 	vq->batch_copy_elems = rte_malloc(NULL,
 				vq->size * sizeof(struct batch_copy_elem),
 				RTE_CACHE_LINE_SIZE);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v17.11 PATCH v2 4/4] vhost: fix possible denial of service by leaking FDs
  2019-11-12 15:19 ` [dpdk-dev] [v17.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 3/4] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
@ 2019-11-12 15:19   ` Maxime Coquelin
  2 siblings, 0 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:19 UTC (permalink / raw)
  To: dev, stable; +Cc: Maxime Coquelin

A malicious Vhost-user master could send in loop hand-crafted
vhost-user messages containing more file descriptors the
vhost-user slave expects. Doing so causes the application using
the vhost-user library to run out of FDs.

This issue has been assigned CVE-2019-14818

Fixes: 8f972312b8f4 ("vhost: support vhost-user")

Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 95 +++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 781734e9e3..d4643dc350 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -81,6 +81,36 @@ static const char *vhost_message_str[VHOST_USER_MAX] = {
 	[VHOST_USER_IOTLB_MSG]  = "VHOST_USER_IOTLB_MSG",
 };
 
+static void
+close_msg_fds(struct VhostUserMsg *msg)
+{
+	int i;
+
+	for (i = 0; i < msg->fd_num; i++)
+		close(msg->fds[i]);
+}
+
+/*
+ * Ensure the expected number of FDs is received,
+ * close all FDs and return an error if this is not the case.
+ */
+static int
+validate_msg_fds(struct VhostUserMsg *msg, int expected_fds)
+{
+	if (msg->fd_num == expected_fds)
+		return 0;
+
+	RTE_LOG(ERR, VHOST_CONFIG,
+		" Expect %d FDs for request %s, received %d\n",
+		expected_fds,
+		vhost_message_str[msg->request.master],
+		msg->fd_num);
+
+	close_msg_fds(msg);
+
+	return -1;
+}
+
 static uint64_t
 get_blk_size(int fd)
 {
@@ -1458,34 +1488,58 @@ vhost_user_msg_handler(int vid, int fd)
 
 	switch (msg.request.master) {
 	case VHOST_USER_GET_FEATURES:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		msg.payload.u64 = vhost_user_get_features(dev);
 		msg.size = sizeof(msg.payload.u64);
 		send_vhost_reply(fd, &msg);
 		break;
 	case VHOST_USER_SET_FEATURES:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_set_features(dev, msg.payload.u64);
 		break;
 
 	case VHOST_USER_GET_PROTOCOL_FEATURES:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_get_protocol_features(dev, &msg);
 		send_vhost_reply(fd, &msg);
 		break;
 	case VHOST_USER_SET_PROTOCOL_FEATURES:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_set_protocol_features(dev, msg.payload.u64);
 		break;
 
 	case VHOST_USER_SET_OWNER:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_set_owner();
 		break;
 	case VHOST_USER_RESET_OWNER:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_reset_owner(dev);
 		break;
 
 	case VHOST_USER_SET_MEM_TABLE:
+		if (validate_msg_fds(&msg, msg.payload.memory.nregions) != 0)
+			return -1;
+
 		ret = vhost_user_set_mem_table(&dev, &msg);
 		break;
 
 	case VHOST_USER_SET_LOG_BASE:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		vhost_user_set_log_base(dev, &msg);
 
 		/*
@@ -1496,61 +1550,102 @@ vhost_user_msg_handler(int vid, int fd)
 		send_vhost_reply(fd, &msg);
 		break;
 	case VHOST_USER_SET_LOG_FD:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		close(msg.fds[0]);
 		RTE_LOG(INFO, VHOST_CONFIG, "not implemented.\n");
 		break;
 
 	case VHOST_USER_SET_VRING_NUM:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_set_vring_num(dev, &msg);
 		break;
 	case VHOST_USER_SET_VRING_ADDR:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_set_vring_addr(&dev, &msg);
 		break;
 	case VHOST_USER_SET_VRING_BASE:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_set_vring_base(dev, &msg);
 		break;
 
 	case VHOST_USER_GET_VRING_BASE:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_get_vring_base(dev, &msg);
 		msg.size = sizeof(msg.payload.state);
 		send_vhost_reply(fd, &msg);
 		break;
 
 	case VHOST_USER_SET_VRING_KICK:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		vhost_user_set_vring_kick(&dev, &msg);
 		break;
 	case VHOST_USER_SET_VRING_CALL:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		vhost_user_set_vring_call(dev, &msg);
 		break;
 
 	case VHOST_USER_SET_VRING_ERR:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		if (!(msg.payload.u64 & VHOST_USER_VRING_NOFD_MASK))
 			close(msg.fds[0]);
 		RTE_LOG(INFO, VHOST_CONFIG, "not implemented\n");
 		break;
 
 	case VHOST_USER_GET_QUEUE_NUM:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
 		msg.payload.u64 = VHOST_MAX_QUEUE_PAIRS;
 		msg.size = sizeof(msg.payload.u64);
 		send_vhost_reply(fd, &msg);
 		break;
 
 	case VHOST_USER_SET_VRING_ENABLE:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_set_vring_enable(dev, &msg);
 		break;
 	case VHOST_USER_SEND_RARP:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		vhost_user_send_rarp(dev, &msg);
 		break;
 
 	case VHOST_USER_NET_SET_MTU:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_net_set_mtu(dev, &msg);
 		break;
 
 	case VHOST_USER_SET_SLAVE_REQ_FD:
+		if (validate_msg_fds(&msg, 1) != 0)
+			return -1;
+
 		ret = vhost_user_set_req_fd(dev, &msg);
 		break;
 
 	case VHOST_USER_IOTLB_MSG:
+		if (validate_msg_fds(&msg, 0) != 0)
+			return -1;
+
 		ret = vhost_user_iotlb_msg(&dev, &msg);
 		break;
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM
  2019-11-12 15:15 [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
  2019-11-12 15:18 ` [dpdk-dev] [v16.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
  2019-11-12 15:19 ` [dpdk-dev] [v17.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
@ 2019-11-12 15:19 ` Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [v18.11 PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
  2019-11-12 15:29   ` [dpdk-dev] [dpdk-stable] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM Kevin Traynor
  2019-11-12 15:19 ` [dpdk-dev] [master " Maxime Coquelin
                   ` (2 subsequent siblings)
  5 siblings, 2 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:19 UTC (permalink / raw)
  To: dev, stable; +Cc: Maxime Coquelin, Jason Wang

vhost_user_set_vring_num() performs multiple allocations
without checking whether data were previously allocated.

It may cause a denial of service because of the memory leaks
that happen if a malicious vhost-user master keeps sending
VHOST_USER_SET_VRING_NUM request until the slave runs out
of memory.

This issue has been assigned CVE-2019-14818

Reported-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 5552f8bbfb..457e62d97e 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -346,6 +346,8 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 		vq->nr_zmbuf = 0;
 		vq->last_zmbuf_idx = 0;
 		vq->zmbuf_size = vq->size;
+		if (vq->zmbufs)
+			rte_free(vq->zmbufs);
 		vq->zmbufs = rte_zmalloc(NULL, vq->zmbuf_size *
 					 sizeof(struct zcopy_mbuf), 0);
 		if (vq->zmbufs == NULL) {
@@ -358,6 +360,8 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 	}
 
 	if (vq_is_packed(dev)) {
+		if (vq->shadow_used_packed)
+			rte_free(vq->shadow_used_packed);
 		vq->shadow_used_packed = rte_malloc(NULL,
 				vq->size *
 				sizeof(struct vring_used_elem_packed),
@@ -369,6 +373,8 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 		}
 
 	} else {
+		if (vq->shadow_used_split)
+			rte_free(vq->shadow_used_split);
 		vq->shadow_used_split = rte_malloc(NULL,
 				vq->size * sizeof(struct vring_used_elem),
 				RTE_CACHE_LINE_SIZE);
@@ -379,6 +385,8 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 		}
 	}
 
+	if (vq->batch_copy_elems)
+		rte_free(vq->batch_copy_elems);
 	vq->batch_copy_elems = rte_malloc(NULL,
 				vq->size * sizeof(struct batch_copy_elem),
 				RTE_CACHE_LINE_SIZE);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [v18.11 PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs
  2019-11-12 15:19 ` [dpdk-dev] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
@ 2019-11-12 15:19   ` Maxime Coquelin
  2019-11-12 15:29     ` [dpdk-dev] [dpdk-stable] " Kevin Traynor
  2019-11-12 15:29   ` [dpdk-dev] [dpdk-stable] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM Kevin Traynor
  1 sibling, 1 reply; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:19 UTC (permalink / raw)
  To: dev, stable; +Cc: Maxime Coquelin

A malicious Vhost-user master could send in loop hand-crafted
vhost-user messages containing more file descriptors the
vhost-user slave expects. Doing so causes the application using
the vhost-user library to run out of FDs.

This issue has been assigned CVE-2019-14818

Fixes: 8f972312b8f4 ("vhost: support vhost-user")

Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 118 ++++++++++++++++++++++++++++++++--
 1 file changed, 114 insertions(+), 4 deletions(-)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 457e62d97e..98cd670e03 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -83,6 +83,36 @@ static const char *vhost_message_str[VHOST_USER_MAX] = {
 static int send_vhost_reply(int sockfd, struct VhostUserMsg *msg);
 static int read_vhost_message(int sockfd, struct VhostUserMsg *msg);
 
+static void
+close_msg_fds(struct VhostUserMsg *msg)
+{
+	int i;
+
+	for (i = 0; i < msg->fd_num; i++)
+		close(msg->fds[i]);
+}
+
+/*
+ * Ensure the expected number of FDs is received,
+ * close all FDs and return an error if this is not the case.
+ */
+static int
+validate_msg_fds(struct VhostUserMsg *msg, int expected_fds)
+{
+	if (msg->fd_num == expected_fds)
+		return 0;
+
+	RTE_LOG(ERR, VHOST_CONFIG,
+		" Expect %d FDs for request %s, received %d\n",
+		expected_fds,
+		vhost_message_str[msg->request.master],
+		msg->fd_num);
+
+	close_msg_fds(msg);
+
+	return -1;
+}
+
 static uint64_t
 get_blk_size(int fd)
 {
@@ -179,18 +209,25 @@ vhost_backend_cleanup(struct virtio_net *dev)
  */
 static int
 vhost_user_set_owner(struct virtio_net **pdev __rte_unused,
-			struct VhostUserMsg *msg __rte_unused,
+			struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	return VH_RESULT_OK;
 }
 
 static int
 vhost_user_reset_owner(struct virtio_net **pdev,
-			struct VhostUserMsg *msg __rte_unused,
+			struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
 	struct virtio_net *dev = *pdev;
+
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	vhost_destroy_device_notify(dev);
 
 	cleanup_device(dev, 0);
@@ -208,6 +245,9 @@ vhost_user_get_features(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct virtio_net *dev = *pdev;
 	uint64_t features = 0;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	rte_vhost_driver_get_features(dev->ifname, &features);
 
 	msg->payload.u64 = features;
@@ -227,6 +267,9 @@ vhost_user_get_queue_num(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct virtio_net *dev = *pdev;
 	uint32_t queue_num = 0;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	rte_vhost_driver_get_queue_num(dev->ifname, &queue_num);
 
 	msg->payload.u64 = (uint64_t)queue_num;
@@ -249,6 +292,9 @@ vhost_user_set_features(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct rte_vdpa_device *vdpa_dev;
 	int did = -1;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	rte_vhost_driver_get_features(dev->ifname, &vhost_features);
 	if (features & ~vhost_features) {
 		RTE_LOG(ERR, VHOST_CONFIG,
@@ -329,6 +375,9 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 	struct virtio_net *dev = *pdev;
 	struct vhost_virtqueue *vq = dev->virtqueue[msg->payload.state.index];
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	vq->size = msg->payload.state.num;
 
 	/* VIRTIO 1.0, 2.4 Virtqueues says:
@@ -708,6 +757,9 @@ vhost_user_set_vring_addr(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct vhost_virtqueue *vq;
 	struct vhost_vring_addr *addr = &msg->payload.addr;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	if (dev->mem == NULL)
 		return VH_RESULT_ERR;
 
@@ -746,6 +798,9 @@ vhost_user_set_vring_base(struct virtio_net **pdev,
 	struct vhost_virtqueue *vq = dev->virtqueue[msg->payload.state.index];
 	uint64_t val = msg->payload.state.num;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	if (vq_is_packed(dev)) {
 		/*
 		 * Bit[0:14]: avail index
@@ -907,6 +962,9 @@ vhost_user_set_mem_table(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	int populate;
 	int fd;
 
+	if (validate_msg_fds(msg, memory->nregions) != 0)
+		return VH_RESULT_ERR;
+
 	if (memory->nregions > VHOST_MEMORY_MAX_NREGIONS) {
 		RTE_LOG(ERR, VHOST_CONFIG,
 			"too many memory regions (%u)\n", memory->nregions);
@@ -917,8 +975,7 @@ vhost_user_set_mem_table(struct virtio_net **pdev, struct VhostUserMsg *msg,
 		RTE_LOG(INFO, VHOST_CONFIG,
 			"(%d) memory regions not changed\n", dev->vid);
 
-		for (i = 0; i < memory->nregions; i++)
-			close(msg->fds[i]);
+		close_msg_fds(msg);
 
 		return VH_RESULT_OK;
 	}
@@ -1061,6 +1118,10 @@ vhost_user_set_mem_table(struct virtio_net **pdev, struct VhostUserMsg *msg,
 				"Failed to read qemu ack on postcopy set-mem-table\n");
 			goto err_mmap;
 		}
+
+		if (validate_msg_fds(&ack_msg, 0) != 0)
+			goto err_mmap;
+
 		if (ack_msg.request.master != VHOST_USER_SET_MEM_TABLE) {
 			RTE_LOG(ERR, VHOST_CONFIG,
 				"Bad qemu ack on postcopy set-mem-table (%d)\n",
@@ -1181,6 +1242,9 @@ vhost_user_set_vring_call(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct vhost_vring_file file;
 	struct vhost_virtqueue *vq;
 
+	if (validate_msg_fds(msg, 1) != 0)
+		return VH_RESULT_ERR;
+
 	file.index = msg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
 	if (msg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)
 		file.fd = VIRTIO_INVALID_EVENTFD;
@@ -1202,6 +1266,9 @@ static int vhost_user_set_vring_err(struct virtio_net **pdev __rte_unused,
 			struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
+	if (validate_msg_fds(msg, 1) != 0)
+		return VH_RESULT_ERR;
+
 	if (!(msg->payload.u64 & VHOST_USER_VRING_NOFD_MASK))
 		close(msg->fds[0]);
 	RTE_LOG(INFO, VHOST_CONFIG, "not implemented\n");
@@ -1217,6 +1284,9 @@ vhost_user_set_vring_kick(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct vhost_vring_file file;
 	struct vhost_virtqueue *vq;
 
+	if (validate_msg_fds(msg, 1) != 0)
+		return VH_RESULT_ERR;
+
 	file.index = msg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
 	if (msg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)
 		file.fd = VIRTIO_INVALID_EVENTFD;
@@ -1273,6 +1343,9 @@ vhost_user_get_vring_base(struct virtio_net **pdev,
 	struct vhost_virtqueue *vq = dev->virtqueue[msg->payload.state.index];
 	uint64_t val;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	/* We have to stop the queue (virtio) if it is running. */
 	vhost_destroy_device_notify(dev);
 
@@ -1346,6 +1419,9 @@ vhost_user_set_vring_enable(struct virtio_net **pdev,
 	struct rte_vdpa_device *vdpa_dev;
 	int did = -1;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	RTE_LOG(INFO, VHOST_CONFIG,
 		"set queue enable: %d to qp idx: %d\n",
 		enable, index);
@@ -1376,6 +1452,9 @@ vhost_user_get_protocol_features(struct virtio_net **pdev,
 	struct virtio_net *dev = *pdev;
 	uint64_t features, protocol_features;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	rte_vhost_driver_get_features(dev->ifname, &features);
 	rte_vhost_driver_get_protocol_features(dev->ifname, &protocol_features);
 
@@ -1404,6 +1483,9 @@ vhost_user_set_protocol_features(struct virtio_net **pdev,
 	uint64_t protocol_features = msg->payload.u64;
 	uint64_t slave_protocol_features = 0;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	rte_vhost_driver_get_protocol_features(dev->ifname,
 			&slave_protocol_features);
 	if (protocol_features & ~slave_protocol_features) {
@@ -1427,6 +1509,9 @@ vhost_user_set_log_base(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	uint64_t size, off;
 	void *addr;
 
+	if (validate_msg_fds(msg, 1) != 0)
+		return VH_RESULT_ERR;
+
 	if (fd < 0) {
 		RTE_LOG(ERR, VHOST_CONFIG, "invalid log fd: %d\n", fd);
 		return VH_RESULT_ERR;
@@ -1490,6 +1575,9 @@ static int vhost_user_set_log_fd(struct virtio_net **pdev __rte_unused,
 			struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
+	if (validate_msg_fds(msg, 1) != 0)
+		return VH_RESULT_ERR;
+
 	close(msg->fds[0]);
 	RTE_LOG(INFO, VHOST_CONFIG, "not implemented.\n");
 
@@ -1513,6 +1601,9 @@ vhost_user_send_rarp(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct rte_vdpa_device *vdpa_dev;
 	int did = -1;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	RTE_LOG(DEBUG, VHOST_CONFIG,
 		":: mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
 		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
@@ -1540,6 +1631,10 @@ vhost_user_net_set_mtu(struct virtio_net **pdev, struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
 	struct virtio_net *dev = *pdev;
+
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	if (msg->payload.u64 < VIRTIO_MIN_MTU ||
 			msg->payload.u64 > VIRTIO_MAX_MTU) {
 		RTE_LOG(ERR, VHOST_CONFIG, "Invalid MTU size (%"PRIu64")\n",
@@ -1560,6 +1655,9 @@ vhost_user_set_req_fd(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct virtio_net *dev = *pdev;
 	int fd = msg->fds[0];
 
+	if (validate_msg_fds(msg, 1) != 0)
+		return VH_RESULT_ERR;
+
 	if (fd < 0) {
 		RTE_LOG(ERR, VHOST_CONFIG,
 				"Invalid file descriptor for slave channel (%d)\n",
@@ -1630,6 +1728,9 @@ vhost_user_iotlb_msg(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	uint16_t i;
 	uint64_t vva, len;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	switch (imsg->type) {
 	case VHOST_IOTLB_UPDATE:
 		len = imsg->size;
@@ -1676,6 +1777,9 @@ vhost_user_set_postcopy_advise(struct virtio_net **pdev,
 #ifdef RTE_LIBRTE_VHOST_POSTCOPY
 	struct uffdio_api api_struct;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	dev->postcopy_ufd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
 
 	if (dev->postcopy_ufd == -1) {
@@ -1711,6 +1815,9 @@ vhost_user_set_postcopy_listen(struct virtio_net **pdev,
 {
 	struct virtio_net *dev = *pdev;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	if (dev->mem && dev->mem->nregions) {
 		RTE_LOG(ERR, VHOST_CONFIG,
 			"Regions already registered at postcopy-listen\n");
@@ -1727,6 +1834,9 @@ vhost_user_postcopy_end(struct virtio_net **pdev, struct VhostUserMsg *msg,
 {
 	struct virtio_net *dev = *pdev;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return VH_RESULT_ERR;
+
 	dev->postcopy_listening = 0;
 	if (dev->postcopy_ufd >= 0) {
 		close(dev->postcopy_ufd);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [master PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM
  2019-11-12 15:15 [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
                   ` (2 preceding siblings ...)
  2019-11-12 15:19 ` [dpdk-dev] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
@ 2019-11-12 15:19 ` Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [master PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
  2019-11-12 15:23   ` [dpdk-dev] [master PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM David Marchand
  2019-11-12 15:35 ` [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
  2019-11-14 11:25 ` [dpdk-dev] [dpdk-security] " Ferruh Yigit
  5 siblings, 2 replies; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:19 UTC (permalink / raw)
  To: dev; +Cc: Maxime Coquelin, Jason Wang

vhost_user_set_vring_num() performs multiple allocations
without checking whether data were previously allocated.

It may cause a denial of service because of the memory leaks
that happen if a malicious vhost-user master keeps sending
VHOST_USER_SET_VRING_NUM request until the slave runs out
of memory.

This issue has been assigned CVE-2019-14818

Fixes: b0a985d1f340 ("vhost: add dequeue zero copy")

Reported-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index ce4e9fb32f..6d2431e604 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -348,6 +348,8 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 		vq->nr_zmbuf = 0;
 		vq->last_zmbuf_idx = 0;
 		vq->zmbuf_size = vq->size;
+		if (vq->zmbufs)
+			rte_free(vq->zmbufs);
 		vq->zmbufs = rte_zmalloc(NULL, vq->zmbuf_size *
 					 sizeof(struct zcopy_mbuf), 0);
 		if (vq->zmbufs == NULL) {
@@ -360,6 +362,8 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 	}
 
 	if (vq_is_packed(dev)) {
+		if (vq->shadow_used_packed)
+			rte_free(vq->shadow_used_packed);
 		vq->shadow_used_packed = rte_malloc(NULL,
 				vq->size *
 				sizeof(struct vring_used_elem_packed),
@@ -371,6 +375,8 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 		}
 
 	} else {
+		if (vq->shadow_used_split)
+			rte_free(vq->shadow_used_split);
 		vq->shadow_used_split = rte_malloc(NULL,
 				vq->size * sizeof(struct vring_used_elem),
 				RTE_CACHE_LINE_SIZE);
@@ -381,6 +387,8 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 		}
 	}
 
+	if (vq->batch_copy_elems)
+		rte_free(vq->batch_copy_elems);
 	vq->batch_copy_elems = rte_malloc(NULL,
 				vq->size * sizeof(struct batch_copy_elem),
 				RTE_CACHE_LINE_SIZE);
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [dpdk-dev] [master PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs
  2019-11-12 15:19 ` [dpdk-dev] [master " Maxime Coquelin
@ 2019-11-12 15:19   ` Maxime Coquelin
  2019-11-12 15:23     ` David Marchand
  2019-11-12 15:23   ` [dpdk-dev] [master PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM David Marchand
  1 sibling, 1 reply; 20+ messages in thread
From: Maxime Coquelin @ 2019-11-12 15:19 UTC (permalink / raw)
  To: dev; +Cc: Maxime Coquelin

A malicious Vhost-user master could send in loop hand-crafted
vhost-user messages containing more file descriptors the
vhost-user slave expects. Doing so causes the application using
the vhost-user library to run out of FDs.

This issue has been assigned CVE-2019-14818

Fixes: 8f972312b8f4 ("vhost: support vhost-user")

Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
---
 lib/librte_vhost/vhost_user.c | 119 ++++++++++++++++++++++++++++++++--
 1 file changed, 115 insertions(+), 4 deletions(-)

diff --git a/lib/librte_vhost/vhost_user.c b/lib/librte_vhost/vhost_user.c
index 6d2431e604..049e37c6de 100644
--- a/lib/librte_vhost/vhost_user.c
+++ b/lib/librte_vhost/vhost_user.c
@@ -83,6 +83,36 @@ static const char *vhost_message_str[VHOST_USER_MAX] = {
 static int send_vhost_reply(int sockfd, struct VhostUserMsg *msg);
 static int read_vhost_message(int sockfd, struct VhostUserMsg *msg);
 
+static void
+close_msg_fds(struct VhostUserMsg *msg)
+{
+	int i;
+
+	for (i = 0; i < msg->fd_num; i++)
+		close(msg->fds[i]);
+}
+
+/*
+ * Ensure the expected number of FDs is received,
+ * close all FDs and return an error if this is not the case.
+ */
+static int
+validate_msg_fds(struct VhostUserMsg *msg, int expected_fds)
+{
+	if (msg->fd_num == expected_fds)
+		return 0;
+
+	RTE_LOG(ERR, VHOST_CONFIG,
+		" Expect %d FDs for request %s, received %d\n",
+		expected_fds,
+		vhost_message_str[msg->request.master],
+		msg->fd_num);
+
+	close_msg_fds(msg);
+
+	return -1;
+}
+
 static uint64_t
 get_blk_size(int fd)
 {
@@ -179,18 +209,25 @@ vhost_backend_cleanup(struct virtio_net *dev)
  */
 static int
 vhost_user_set_owner(struct virtio_net **pdev __rte_unused,
-			struct VhostUserMsg *msg __rte_unused,
+			struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	return RTE_VHOST_MSG_RESULT_OK;
 }
 
 static int
 vhost_user_reset_owner(struct virtio_net **pdev,
-			struct VhostUserMsg *msg __rte_unused,
+			struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
 	struct virtio_net *dev = *pdev;
+
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	vhost_destroy_device_notify(dev);
 
 	cleanup_device(dev, 0);
@@ -208,6 +245,9 @@ vhost_user_get_features(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct virtio_net *dev = *pdev;
 	uint64_t features = 0;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	rte_vhost_driver_get_features(dev->ifname, &features);
 
 	msg->payload.u64 = features;
@@ -227,6 +267,9 @@ vhost_user_get_queue_num(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct virtio_net *dev = *pdev;
 	uint32_t queue_num = 0;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	rte_vhost_driver_get_queue_num(dev->ifname, &queue_num);
 
 	msg->payload.u64 = (uint64_t)queue_num;
@@ -249,6 +292,9 @@ vhost_user_set_features(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct rte_vdpa_device *vdpa_dev;
 	int did = -1;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	rte_vhost_driver_get_features(dev->ifname, &vhost_features);
 	if (features & ~vhost_features) {
 		RTE_LOG(ERR, VHOST_CONFIG,
@@ -331,6 +377,9 @@ vhost_user_set_vring_num(struct virtio_net **pdev,
 	struct virtio_net *dev = *pdev;
 	struct vhost_virtqueue *vq = dev->virtqueue[msg->payload.state.index];
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	vq->size = msg->payload.state.num;
 
 	/* VIRTIO 1.0, 2.4 Virtqueues says:
@@ -718,6 +767,9 @@ vhost_user_set_vring_addr(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct vhost_vring_addr *addr = &msg->payload.addr;
 	bool access_ok;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	if (dev->mem == NULL)
 		return RTE_VHOST_MSG_RESULT_ERR;
 
@@ -759,6 +811,9 @@ vhost_user_set_vring_base(struct virtio_net **pdev,
 	struct vhost_virtqueue *vq = dev->virtqueue[msg->payload.state.index];
 	uint64_t val = msg->payload.state.num;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	if (vq_is_packed(dev)) {
 		/*
 		 * Bit[0:14]: avail index
@@ -920,6 +975,9 @@ vhost_user_set_mem_table(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	int populate;
 	int fd;
 
+	if (validate_msg_fds(msg, memory->nregions) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	if (memory->nregions > VHOST_MEMORY_MAX_NREGIONS) {
 		RTE_LOG(ERR, VHOST_CONFIG,
 			"too many memory regions (%u)\n", memory->nregions);
@@ -930,8 +988,7 @@ vhost_user_set_mem_table(struct virtio_net **pdev, struct VhostUserMsg *msg,
 		RTE_LOG(INFO, VHOST_CONFIG,
 			"(%d) memory regions not changed\n", dev->vid);
 
-		for (i = 0; i < memory->nregions; i++)
-			close(msg->fds[i]);
+		close_msg_fds(msg);
 
 		return RTE_VHOST_MSG_RESULT_OK;
 	}
@@ -1074,6 +1131,10 @@ vhost_user_set_mem_table(struct virtio_net **pdev, struct VhostUserMsg *msg,
 				"Failed to read qemu ack on postcopy set-mem-table\n");
 			goto err_mmap;
 		}
+
+		if (validate_msg_fds(&ack_msg, 0) != 0)
+			goto err_mmap;
+
 		if (ack_msg.request.master != VHOST_USER_SET_MEM_TABLE) {
 			RTE_LOG(ERR, VHOST_CONFIG,
 				"Bad qemu ack on postcopy set-mem-table (%d)\n",
@@ -1194,6 +1255,9 @@ vhost_user_set_vring_call(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct vhost_vring_file file;
 	struct vhost_virtqueue *vq;
 
+	if (validate_msg_fds(msg, 1) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	file.index = msg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
 	if (msg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)
 		file.fd = VIRTIO_INVALID_EVENTFD;
@@ -1215,6 +1279,9 @@ static int vhost_user_set_vring_err(struct virtio_net **pdev __rte_unused,
 			struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
+	if (validate_msg_fds(msg, 1) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	if (!(msg->payload.u64 & VHOST_USER_VRING_NOFD_MASK))
 		close(msg->fds[0]);
 	RTE_LOG(INFO, VHOST_CONFIG, "not implemented\n");
@@ -1230,6 +1297,9 @@ vhost_user_set_vring_kick(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct vhost_vring_file file;
 	struct vhost_virtqueue *vq;
 
+	if (validate_msg_fds(msg, 1) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	file.index = msg->payload.u64 & VHOST_USER_VRING_IDX_MASK;
 	if (msg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)
 		file.fd = VIRTIO_INVALID_EVENTFD;
@@ -1286,6 +1356,9 @@ vhost_user_get_vring_base(struct virtio_net **pdev,
 	struct vhost_virtqueue *vq = dev->virtqueue[msg->payload.state.index];
 	uint64_t val;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	/* We have to stop the queue (virtio) if it is running. */
 	vhost_destroy_device_notify(dev);
 
@@ -1361,6 +1434,9 @@ vhost_user_set_vring_enable(struct virtio_net **pdev,
 	struct rte_vdpa_device *vdpa_dev;
 	int did = -1;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	RTE_LOG(INFO, VHOST_CONFIG,
 		"set queue enable: %d to qp idx: %d\n",
 		enable, index);
@@ -1391,6 +1467,9 @@ vhost_user_get_protocol_features(struct virtio_net **pdev,
 	struct virtio_net *dev = *pdev;
 	uint64_t features, protocol_features;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	rte_vhost_driver_get_features(dev->ifname, &features);
 	rte_vhost_driver_get_protocol_features(dev->ifname, &protocol_features);
 
@@ -1419,6 +1498,9 @@ vhost_user_set_protocol_features(struct virtio_net **pdev,
 	uint64_t protocol_features = msg->payload.u64;
 	uint64_t slave_protocol_features = 0;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	rte_vhost_driver_get_protocol_features(dev->ifname,
 			&slave_protocol_features);
 	if (protocol_features & ~slave_protocol_features) {
@@ -1445,6 +1527,9 @@ vhost_user_set_log_base(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	uint64_t size, off;
 	void *addr;
 
+	if (validate_msg_fds(msg, 1) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	if (fd < 0) {
 		RTE_LOG(ERR, VHOST_CONFIG, "invalid log fd: %d\n", fd);
 		return RTE_VHOST_MSG_RESULT_ERR;
@@ -1508,6 +1593,9 @@ static int vhost_user_set_log_fd(struct virtio_net **pdev __rte_unused,
 			struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
+	if (validate_msg_fds(msg, 1) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	close(msg->fds[0]);
 	RTE_LOG(INFO, VHOST_CONFIG, "not implemented.\n");
 
@@ -1531,6 +1619,9 @@ vhost_user_send_rarp(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct rte_vdpa_device *vdpa_dev;
 	int did = -1;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	RTE_LOG(DEBUG, VHOST_CONFIG,
 		":: mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
 		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
@@ -1558,6 +1649,10 @@ vhost_user_net_set_mtu(struct virtio_net **pdev, struct VhostUserMsg *msg,
 			int main_fd __rte_unused)
 {
 	struct virtio_net *dev = *pdev;
+
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	if (msg->payload.u64 < VIRTIO_MIN_MTU ||
 			msg->payload.u64 > VIRTIO_MAX_MTU) {
 		RTE_LOG(ERR, VHOST_CONFIG, "Invalid MTU size (%"PRIu64")\n",
@@ -1578,6 +1673,9 @@ vhost_user_set_req_fd(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	struct virtio_net *dev = *pdev;
 	int fd = msg->fds[0];
 
+	if (validate_msg_fds(msg, 1) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	if (fd < 0) {
 		RTE_LOG(ERR, VHOST_CONFIG,
 				"Invalid file descriptor for slave channel (%d)\n",
@@ -1663,6 +1761,9 @@ vhost_user_iotlb_msg(struct virtio_net **pdev, struct VhostUserMsg *msg,
 	uint16_t i;
 	uint64_t vva, len;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	switch (imsg->type) {
 	case VHOST_IOTLB_UPDATE:
 		len = imsg->size;
@@ -1709,6 +1810,9 @@ vhost_user_set_postcopy_advise(struct virtio_net **pdev,
 #ifdef RTE_LIBRTE_VHOST_POSTCOPY
 	struct uffdio_api api_struct;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	dev->postcopy_ufd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
 
 	if (dev->postcopy_ufd == -1) {
@@ -1744,6 +1848,9 @@ vhost_user_set_postcopy_listen(struct virtio_net **pdev,
 {
 	struct virtio_net *dev = *pdev;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	if (dev->mem && dev->mem->nregions) {
 		RTE_LOG(ERR, VHOST_CONFIG,
 			"Regions already registered at postcopy-listen\n");
@@ -1760,6 +1867,9 @@ vhost_user_postcopy_end(struct virtio_net **pdev, struct VhostUserMsg *msg,
 {
 	struct virtio_net *dev = *pdev;
 
+	if (validate_msg_fds(msg, 0) != 0)
+		return RTE_VHOST_MSG_RESULT_ERR;
+
 	dev->postcopy_listening = 0;
 	if (dev->postcopy_ufd >= 0) {
 		close(dev->postcopy_ufd);
@@ -2112,6 +2222,7 @@ vhost_user_msg_handler(int vid, int fd)
 	if (!handled) {
 		RTE_LOG(ERR, VHOST_CONFIG,
 			"vhost message (req: %d) was not handled.\n", request);
+		close_msg_fds(&msg);
 		ret = RTE_VHOST_MSG_RESULT_ERR;
 	}
 
-- 
2.21.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [dpdk-dev] [master PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM
  2019-11-12 15:19 ` [dpdk-dev] [master " Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [master PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
@ 2019-11-12 15:23   ` David Marchand
  1 sibling, 0 replies; 20+ messages in thread
From: David Marchand @ 2019-11-12 15:23 UTC (permalink / raw)
  To: Maxime Coquelin, dev; +Cc: Jason Wang

On 12/11/2019 16:19, Maxime Coquelin wrote:
> vhost_user_set_vring_num() performs multiple allocations
> without checking whether data were previously allocated.
> 
> It may cause a denial of service because of the memory leaks
> that happen if a malicious vhost-user master keeps sending
> VHOST_USER_SET_VRING_NUM request until the slave runs out
> of memory.
> 
> This issue has been assigned CVE-2019-14818
> 
> Fixes: b0a985d1f340 ("vhost: add dequeue zero copy")
> 
> Reported-by: Jason Wang <jasowang@redhat.com>
> Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>

Applied, thanks.


-- 
David Marchand


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [dpdk-dev] [master PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs
  2019-11-12 15:19   ` [dpdk-dev] [master PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
@ 2019-11-12 15:23     ` David Marchand
  0 siblings, 0 replies; 20+ messages in thread
From: David Marchand @ 2019-11-12 15:23 UTC (permalink / raw)
  To: Maxime Coquelin, dev

On 12/11/2019 16:19, Maxime Coquelin wrote:
> A malicious Vhost-user master could send in loop hand-crafted
> vhost-user messages containing more file descriptors the
> vhost-user slave expects. Doing so causes the application using
> the vhost-user library to run out of FDs.
> 
> This issue has been assigned CVE-2019-14818
> 
> Fixes: 8f972312b8f4 ("vhost: support vhost-user")
> 
> Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>

Applied, thanks.


-- 
David Marchand


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [dpdk-dev] [dpdk-stable] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM
  2019-11-12 15:19 ` [dpdk-dev] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
  2019-11-12 15:19   ` [dpdk-dev] [v18.11 PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
@ 2019-11-12 15:29   ` Kevin Traynor
  1 sibling, 0 replies; 20+ messages in thread
From: Kevin Traynor @ 2019-11-12 15:29 UTC (permalink / raw)
  To: Maxime Coquelin, dev, stable; +Cc: Jason Wang

On 12/11/2019 15:19, Maxime Coquelin wrote:
> vhost_user_set_vring_num() performs multiple allocations
> without checking whether data were previously allocated.
> 
> It may cause a denial of service because of the memory leaks
> that happen if a malicious vhost-user master keeps sending
> VHOST_USER_SET_VRING_NUM request until the slave runs out
> of memory.
> 
> This issue has been assigned CVE-2019-14818
> 
> Reported-by: Jason Wang <jasowang@redhat.com>
> Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
> ---

Applied, thanks.


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [dpdk-dev] [dpdk-stable] [v18.11 PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs
  2019-11-12 15:19   ` [dpdk-dev] [v18.11 PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
@ 2019-11-12 15:29     ` Kevin Traynor
  0 siblings, 0 replies; 20+ messages in thread
From: Kevin Traynor @ 2019-11-12 15:29 UTC (permalink / raw)
  To: Maxime Coquelin, dev, stable

On 12/11/2019 15:19, Maxime Coquelin wrote:
> A malicious Vhost-user master could send in loop hand-crafted
> vhost-user messages containing more file descriptors the
> vhost-user slave expects. Doing so causes the application using
> the vhost-user library to run out of FDs.
> 
> This issue has been assigned CVE-2019-14818
> 
> Fixes: 8f972312b8f4 ("vhost: support vhost-user")
> 
> Signed-off-by: Maxime Coquelin <maxime.coquelin@redhat.com>
> ---

Applied, thanks.


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [dpdk-dev] DPDK security advisory: CVE-2019-14818
  2019-11-12 15:15 [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
                   ` (3 preceding siblings ...)
  2019-11-12 15:19 ` [dpdk-dev] [master " Maxime Coquelin
@ 2019-11-12 15:35 ` Ferruh Yigit
  2019-11-14 11:25 ` [dpdk-dev] [dpdk-security] " Ferruh Yigit
  5 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2019-11-12 15:35 UTC (permalink / raw)
  To: dpdk-dev; +Cc: security, security-prerelease, dpdk-announce, Jason Wang

On 11/12/2019 3:15 PM, Ferruh Yigit wrote:
> A vulnerability was fixed in DPDK.
> 
> Some downstream stakeholders were warned in advance in order to coordinate the
> release of fixes and reduce the vulnerability window.
> 
> Problem:
> A malicious container which has direct access to the vhost-user socket can keep
> sending messages which may cause leaking resources until resulting a DOS.
> 
> All users of the vhost library are strongly encouraged to upgrade as soon as
> possible.
> 
> CVE-2019-14818
> Bugzilla: https://bugs.dpdk.org/show_bug.cgi?id=363
> Severity: Medium
> CVSS scores: 6.8

And thanks to the "Jason Wang" [1] for reporting the vulnerability, all credits
for discovering the issue goes to him.

[1]
Jason Wang <jasowang@redhat.com>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [dpdk-dev] [dpdk-security] DPDK security advisory: CVE-2019-14818
  2019-11-12 15:15 [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
                   ` (4 preceding siblings ...)
  2019-11-12 15:35 ` [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
@ 2019-11-14 11:25 ` Ferruh Yigit
  2019-11-15 18:19   ` Ferruh Yigit
  5 siblings, 1 reply; 20+ messages in thread
From: Ferruh Yigit @ 2019-11-14 11:25 UTC (permalink / raw)
  To: dpdk-announce; +Cc: dpdk-dev, security, security-prerelease

On 11/12/2019 3:15 PM, Ferruh Yigit wrote:
> A vulnerability was fixed in DPDK.
> 
> Some downstream stakeholders were warned in advance in order to coordinate the
> release of fixes and reduce the vulnerability window.
> 
> Problem:
> A malicious container which has direct access to the vhost-user socket can keep
> sending messages which may cause leaking resources until resulting a DOS.
> 
> All users of the vhost library are strongly encouraged to upgrade as soon as
> possible.
> 
> CVE-2019-14818
> Bugzilla: https://bugs.dpdk.org/show_bug.cgi?id=363
> Severity: Medium
> CVSS scores: 6.8
> 
> 
> 
> Commits:
> main repo
> https://git.dpdk.org/dpdk/commit/?id=612e17cf6d7b
> https://git.dpdk.org/dpdk/commit/?id=bf472259dde6
> 
> 19.08.1
> https://git.dpdk.org/dpdk-stable/commit/?h=19.08&id=fa674d08985f
> https://git.dpdk.org/dpdk-stable/commit/?h=19.08&id=6547dd563ea9
> 
> 18.11.4 (LTS)
> https://git.dpdk.org/dpdk-stable/commit/?h=18.11&id=70583a6b9b1c
> https://git.dpdk.org/dpdk-stable/commit/?h=18.11&id=f8898927bb16
> 
> 17.11.8 (LTS)
> https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=3b1b44a1c82a
> https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=8a8dbd0ec19e
> https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=1f6147d9a01f
> 
> 16.11.10 (LTS EOL)
> https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=5fbb5c2919b6
> https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=3863340f93b8
> https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=8790f4c3bcd2
> https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=1bf11cfb7c7c
> 

A regression has been found on the above commits when VHOST_USER_VRING_NOFD_MASK
is set, there is a suggested fix [1], review and testing is going on.
We are planning to have an update tomorrow.

Sorry for the inconvenience caused.

[1]
https://patches.dpdk.org/patch/62956/

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [dpdk-dev] [dpdk-security] DPDK security advisory: CVE-2019-14818
  2019-11-14 11:25 ` [dpdk-dev] [dpdk-security] " Ferruh Yigit
@ 2019-11-15 18:19   ` Ferruh Yigit
  0 siblings, 0 replies; 20+ messages in thread
From: Ferruh Yigit @ 2019-11-15 18:19 UTC (permalink / raw)
  To: dpdk-announce; +Cc: dpdk-dev, security, security-prerelease

On 11/14/2019 11:25 AM, Ferruh Yigit wrote:
> On 11/12/2019 3:15 PM, Ferruh Yigit wrote:
>> A vulnerability was fixed in DPDK.
>>
>> Some downstream stakeholders were warned in advance in order to coordinate the
>> release of fixes and reduce the vulnerability window.
>>
>> Problem:
>> A malicious container which has direct access to the vhost-user socket can keep
>> sending messages which may cause leaking resources until resulting a DOS.
>>
>> All users of the vhost library are strongly encouraged to upgrade as soon as
>> possible.
>>
>> CVE-2019-14818
>> Bugzilla: https://bugs.dpdk.org/show_bug.cgi?id=363
>> Severity: Medium
>> CVSS scores: 6.8
>>

<...>

>>
> 
> A regression has been found on the above commits when VHOST_USER_VRING_NOFD_MASK
> is set, there is a suggested fix [1], review and testing is going on.
> We are planning to have an update tomorrow.
> 
> Sorry for the inconvenience caused.
> 
> [1]
> https://patches.dpdk.org/patch/62956/
> 

Regression has been solved, please find new stable release links and updated
commits.


Commits:
main repo
https://git.dpdk.org/dpdk/commit/?id=612e17cf6d7b
https://git.dpdk.org/dpdk/commit/?id=bf472259dde6
https://git.dpdk.org/dpdk/commit/?id=1407b0752eee

19.08.2
https://git.dpdk.org/dpdk-stable/commit/?h=19.08&id=fa674d08985f
https://git.dpdk.org/dpdk-stable/commit/?h=19.08&id=6547dd563ea9
https://git.dpdk.org/dpdk-stable/commit/?h=19.08&id=7ce55a8e4f4d

18.11.5 (LTS)
https://git.dpdk.org/dpdk-stable/commit/?h=18.11&id=70583a6b9b1c
https://git.dpdk.org/dpdk-stable/commit/?h=18.11&id=f8898927bb16
https://git.dpdk.org/dpdk-stable/commit/?h=18.11&id=afc8c11865ef

17.11.9 (LTS)
https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=3b1b44a1c82a
https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=8a8dbd0ec19e
https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=1f6147d9a01f
https://git.dpdk.org/dpdk-stable/commit/?h=17.11&id=89ce028931ef

16.11.11 (LTS EOL)
https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=5fbb5c2919b6
https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=3863340f93b8
https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=8790f4c3bcd2
https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=1bf11cfb7c7c
https://git.dpdk.org/dpdk-stable/commit/?h=16.11&id=25b8ea4d8604


Stable Releases download links:
DPDK 19.08.2
http://fast.dpdk.org/rel/dpdk-19.08.2.tar.xz

DPDK 18.11.5 (LTS)
http://fast.dpdk.org/rel/dpdk-18.11.5.tar.xz

DPDK 17.11.9 (LTS)
http://fast.dpdk.org/rel/dpdk-17.11.9.tar.xz

DPDK 16.11.11 (LTS EOL)
http://fast.dpdk.org/rel/dpdk-16.11.11.tar.xz

-- 
DPDK Security Team
http://core.dpdk.org/security/

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2019-11-15 18:19 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-12 15:15 [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
2019-11-12 15:18 ` [dpdk-dev] [v16.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages Maxime Coquelin
2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 3/4] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
2019-11-12 15:18   ` [dpdk-dev] [v16.11 PATCH v2 4/4] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
2019-11-12 15:19 ` [dpdk-dev] [v17.11 PATCH v2 1/4] vhost: validate virtqueue size Maxime Coquelin
2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 2/4] vhost: add number of fds to vhost-user messages Maxime Coquelin
2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 3/4] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
2019-11-12 15:19   ` [dpdk-dev] [v17.11 PATCH v2 4/4] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
2019-11-12 15:19 ` [dpdk-dev] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM Maxime Coquelin
2019-11-12 15:19   ` [dpdk-dev] [v18.11 PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
2019-11-12 15:29     ` [dpdk-dev] [dpdk-stable] " Kevin Traynor
2019-11-12 15:29   ` [dpdk-dev] [dpdk-stable] [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM Kevin Traynor
2019-11-12 15:19 ` [dpdk-dev] [master " Maxime Coquelin
2019-11-12 15:19   ` [dpdk-dev] [master PATCH v2 2/2] vhost: fix possible denial of service by leaking FDs Maxime Coquelin
2019-11-12 15:23     ` David Marchand
2019-11-12 15:23   ` [dpdk-dev] [master PATCH v2 1/2] vhost: fix possible denial of service on SET_VRING_NUM David Marchand
2019-11-12 15:35 ` [dpdk-dev] DPDK security advisory: CVE-2019-14818 Ferruh Yigit
2019-11-14 11:25 ` [dpdk-dev] [dpdk-security] " Ferruh Yigit
2019-11-15 18:19   ` Ferruh Yigit

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).