Version in base suite: 18.11.2-2+deb10u1 Base version: dpdk_18.11.2-2+deb10u1 Target version: dpdk_18.11.2-2+deb10u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/d/dpdk/dpdk_18.11.2-2+deb10u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/d/dpdk/dpdk_18.11.2-2+deb10u2.dsc changelog | 7 patches/0001-vhost-fix-possible-denial-of-service-on-SET_VRING_NU.patch | 65 + patches/0002-vhost-fix-possible-denial-of-service-by-leaking-FDs.patch | 344 ++++++++++ patches/series | 2 4 files changed, 418 insertions(+) diff -Nru dpdk-18.11.2/debian/changelog dpdk-18.11.2/debian/changelog --- dpdk-18.11.2/debian/changelog 2019-08-09 12:35:31.000000000 +0000 +++ dpdk-18.11.2/debian/changelog 2019-11-12 10:56:45.000000000 +0000 @@ -1,3 +1,10 @@ +dpdk (18.11.2-2+deb10u2) buster-security; urgency=high + + * Backport patches to fix CVE-2019-14818. A denial of service security + issue has been found in the Vhost PMD. + + -- Luca Boccassi Tue, 12 Nov 2019 10:56:45 +0000 + dpdk (18.11.2-2+deb10u1) buster; urgency=medium * Rebuild for buster. diff -Nru dpdk-18.11.2/debian/patches/0001-vhost-fix-possible-denial-of-service-on-SET_VRING_NU.patch dpdk-18.11.2/debian/patches/0001-vhost-fix-possible-denial-of-service-on-SET_VRING_NU.patch --- dpdk-18.11.2/debian/patches/0001-vhost-fix-possible-denial-of-service-on-SET_VRING_NU.patch 1970-01-01 00:00:00.000000000 +0000 +++ dpdk-18.11.2/debian/patches/0001-vhost-fix-possible-denial-of-service-on-SET_VRING_NU.patch 2019-11-12 10:56:45.000000000 +0000 @@ -0,0 +1,65 @@ +From f110daae0d7d033db151d2791f6555546d5144ac Mon Sep 17 00:00:00 2001 +From: Maxime Coquelin +Date: Fri, 23 Aug 2019 15:17:05 +0200 +Subject: [v18.11 PATCH v2 1/2] vhost: fix possible denial of service on + SET_VRING_NUM + +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 +Signed-off-by: Maxime Coquelin +--- + 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 + diff -Nru dpdk-18.11.2/debian/patches/0002-vhost-fix-possible-denial-of-service-by-leaking-FDs.patch dpdk-18.11.2/debian/patches/0002-vhost-fix-possible-denial-of-service-by-leaking-FDs.patch --- dpdk-18.11.2/debian/patches/0002-vhost-fix-possible-denial-of-service-by-leaking-FDs.patch 1970-01-01 00:00:00.000000000 +0000 +++ dpdk-18.11.2/debian/patches/0002-vhost-fix-possible-denial-of-service-by-leaking-FDs.patch 2019-11-12 10:56:45.000000000 +0000 @@ -0,0 +1,344 @@ +From 8accec7a78708ee8ece4550e370d32ccdff4dee8 Mon Sep 17 00:00:00 2001 +From: Maxime Coquelin +Date: Tue, 3 Sep 2019 17:34:22 +0200 +Subject: [v18.11 PATCH v2 2/2] vhost: fix possible denial of service by + leaking FDs + +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 +--- + 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 + diff -Nru dpdk-18.11.2/debian/patches/series dpdk-18.11.2/debian/patches/series --- dpdk-18.11.2/debian/patches/series 2019-08-06 20:31:48.000000000 +0000 +++ dpdk-18.11.2/debian/patches/series 2019-11-12 10:56:45.000000000 +0000 @@ -2,3 +2,5 @@ 0005-build-use-dependency-instead-of-find_library.patch 0006-build-reorder-libraries-and-build-eal-before-cmdline.patch 0007-build-use-dependency-for-libbsd-instead-of-manual-ap.patch +0001-vhost-fix-possible-denial-of-service-on-SET_VRING_NU.patch +0002-vhost-fix-possible-denial-of-service-by-leaking-FDs.patch