vhost: fix possible denial of service by leaking FDs
[dpdk.git] / lib / librte_vhost / vhost_user.c
index ce4e9fb..90ecee1 100644 (file)
 #ifdef RTE_LIBRTE_VHOST_POSTCOPY
 #include <linux/userfaultfd.h>
 #endif
+#ifdef F_ADD_SEALS /* if file sealing is supported, so is memfd */
+#include <linux/memfd.h>
+#define MEMFD_SUPPORTED
+#endif
 
 #include <rte_common.h>
 #include <rte_malloc.h>
@@ -49,6 +53,9 @@
 #define VIRTIO_MIN_MTU 68
 #define VIRTIO_MAX_MTU 65535
 
+#define INFLIGHT_ALIGNMENT     64
+#define INFLIGHT_VERSION       0x1
+
 static const char *vhost_message_str[VHOST_USER_MAX] = {
        [VHOST_USER_NONE] = "VHOST_USER_NONE",
        [VHOST_USER_GET_FEATURES] = "VHOST_USER_GET_FEATURES",
@@ -78,11 +85,43 @@ static const char *vhost_message_str[VHOST_USER_MAX] = {
        [VHOST_USER_POSTCOPY_ADVISE]  = "VHOST_USER_POSTCOPY_ADVISE",
        [VHOST_USER_POSTCOPY_LISTEN]  = "VHOST_USER_POSTCOPY_LISTEN",
        [VHOST_USER_POSTCOPY_END]  = "VHOST_USER_POSTCOPY_END",
+       [VHOST_USER_GET_INFLIGHT_FD] = "VHOST_USER_GET_INFLIGHT_FD",
+       [VHOST_USER_SET_INFLIGHT_FD] = "VHOST_USER_SET_INFLIGHT_FD",
 };
 
 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)
 {
@@ -160,6 +199,22 @@ vhost_backend_cleanup(struct virtio_net *dev)
                dev->log_addr = 0;
        }
 
+       if (dev->inflight_info) {
+               if (dev->inflight_info->addr) {
+                       munmap(dev->inflight_info->addr,
+                              dev->inflight_info->size);
+                       dev->inflight_info->addr = NULL;
+               }
+
+               if (dev->inflight_info->fd > 0) {
+                       close(dev->inflight_info->fd);
+                       dev->inflight_info->fd = -1;
+               }
+
+               free(dev->inflight_info);
+               dev->inflight_info = NULL;
+       }
+
        if (dev->slave_req_fd >= 0) {
                close(dev->slave_req_fd);
                dev->slave_req_fd = -1;
@@ -179,18 +234,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 +270,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 +292,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 +317,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,
@@ -308,6 +379,7 @@ vhost_user_set_features(struct virtio_net **pdev, struct VhostUserMsg *msg,
 
                        dev->virtqueue[dev->nr_vring] = NULL;
                        cleanup_vq(vq, 1);
+                       cleanup_vq_inflight(dev, vq);
                        free_vq(dev, vq);
                }
        }
@@ -331,14 +403,29 @@ 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:
         *
         *   Queue Size value is always a power of 2. The maximum Queue Size
         *   value is 32768.
+        *
+        * VIRTIO 1.1 2.7 Virtqueues says:
+        *
+        *   Packed virtqueues support up to 2^15 entries each.
         */
-       if ((vq->size & (vq->size - 1)) || vq->size > 32768) {
+       if (!vq_is_packed(dev)) {
+               if (vq->size & (vq->size - 1)) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "invalid virtqueue size %u\n", vq->size);
+                       return RTE_VHOST_MSG_RESULT_ERR;
+               }
+       }
+
+       if (vq->size > 32768) {
                RTE_LOG(ERR, VHOST_CONFIG,
                        "invalid virtqueue size %u\n", vq->size);
                return RTE_VHOST_MSG_RESULT_ERR;
@@ -348,6 +435,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 +449,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 +462,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 +474,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);
@@ -575,6 +670,39 @@ ring_addr_to_vva(struct virtio_net *dev, struct vhost_virtqueue *vq,
        return qva_to_vva(dev, ra, size);
 }
 
+/*
+ * Converts vring log address to GPA
+ * If IOMMU is enabled, the log address is IOVA
+ * If IOMMU not enabled, the log address is already GPA
+ */
+static uint64_t
+translate_log_addr(struct virtio_net *dev, struct vhost_virtqueue *vq,
+               uint64_t log_addr)
+{
+       if (dev->features & (1ULL << VIRTIO_F_IOMMU_PLATFORM)) {
+               const uint64_t exp_size = sizeof(struct vring_used) +
+                       sizeof(struct vring_used_elem) * vq->size;
+               uint64_t hva, gpa;
+               uint64_t size = exp_size;
+
+               hva = vhost_iova_to_vva(dev, vq, log_addr,
+                                       &size, VHOST_ACCESS_RW);
+               if (size != exp_size)
+                       return 0;
+
+               gpa = hva_to_gpa(dev, hva, exp_size);
+               if (!gpa) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "VQ: Failed to find GPA for log_addr: 0x%" PRIx64 " hva: 0x%" PRIx64 "\n",
+                               log_addr, hva);
+                       return 0;
+               }
+               return gpa;
+
+       } else
+               return log_addr;
+}
+
 static struct virtio_net *
 translate_ring_addresses(struct virtio_net *dev, int vq_index)
 {
@@ -582,11 +710,21 @@ translate_ring_addresses(struct virtio_net *dev, int vq_index)
        struct vhost_vring_addr *addr = &vq->ring_addrs;
        uint64_t len, expected_len;
 
+       if (addr->flags & (1 << VHOST_VRING_F_LOG)) {
+               vq->log_guest_addr =
+                       translate_log_addr(dev, vq, addr->log_guest_addr);
+               if (vq->log_guest_addr == 0) {
+                       RTE_LOG(DEBUG, VHOST_CONFIG,
+                               "(%d) failed to map log_guest_addr.\n",
+                               dev->vid);
+                       return dev;
+               }
+       }
+
        if (vq_is_packed(dev)) {
                len = sizeof(struct vring_packed_desc) * vq->size;
                vq->desc_packed = (struct vring_packed_desc *)(uintptr_t)
                        ring_addr_to_vva(dev, vq, addr->desc_user_addr, &len);
-               vq->log_guest_addr = 0;
                if (vq->desc_packed == NULL ||
                                len != sizeof(struct vring_packed_desc) *
                                vq->size) {
@@ -682,7 +820,6 @@ translate_ring_addresses(struct virtio_net *dev, int vq_index)
                vq->last_avail_idx = vq->used->idx;
        }
 
-       vq->log_guest_addr = addr->log_guest_addr;
        vq->access_ok = 1;
 
        VHOST_LOG_DEBUG(VHOST_CONFIG, "(%d) mapped address desc: %p\n",
@@ -710,6 +847,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;
 
@@ -751,6 +891,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
@@ -912,6 +1055,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);
@@ -922,8 +1068,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;
        }
@@ -1066,6 +1211,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",
@@ -1097,10 +1246,11 @@ vhost_user_set_mem_table(struct virtio_net **pdev, struct VhostUserMsg *msg,
                                goto err_mmap;
                        }
                        RTE_LOG(INFO, VHOST_CONFIG,
-                               "\t userfaultfd registered for range : %llx - %llx\n",
-                               reg_struct.range.start,
-                               reg_struct.range.start +
-                               reg_struct.range.len - 1);
+                               "\t userfaultfd registered for range : "
+                               "%" PRIx64 " - %" PRIx64 "\n",
+                               (uint64_t)reg_struct.range.start,
+                               (uint64_t)reg_struct.range.start +
+                               (uint64_t)reg_struct.range.len - 1);
 #else
                        goto err_mmap;
 #endif
@@ -1178,6 +1328,234 @@ virtio_is_ready(struct virtio_net *dev)
        return 1;
 }
 
+static void *
+inflight_mem_alloc(const char *name, size_t size, int *fd)
+{
+       void *ptr;
+       int mfd = -1;
+       char fname[20] = "/tmp/memfd-XXXXXX";
+
+       *fd = -1;
+#ifdef MEMFD_SUPPORTED
+       mfd = memfd_create(name, MFD_CLOEXEC);
+#else
+       RTE_SET_USED(name);
+#endif
+       if (mfd == -1) {
+               mfd = mkstemp(fname);
+               if (mfd == -1) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "failed to get inflight buffer fd\n");
+                       return NULL;
+               }
+
+               unlink(fname);
+       }
+
+       if (ftruncate(mfd, size) == -1) {
+               RTE_LOG(ERR, VHOST_CONFIG,
+                       "failed to alloc inflight buffer\n");
+               close(mfd);
+               return NULL;
+       }
+
+       ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0);
+       if (ptr == MAP_FAILED) {
+               RTE_LOG(ERR, VHOST_CONFIG,
+                       "failed to mmap inflight buffer\n");
+               close(mfd);
+               return NULL;
+       }
+
+       *fd = mfd;
+       return ptr;
+}
+
+static uint32_t
+get_pervq_shm_size_split(uint16_t queue_size)
+{
+       return RTE_ALIGN_MUL_CEIL(sizeof(struct rte_vhost_inflight_desc_split) *
+                                 queue_size + sizeof(uint64_t) +
+                                 sizeof(uint16_t) * 4, INFLIGHT_ALIGNMENT);
+}
+
+static uint32_t
+get_pervq_shm_size_packed(uint16_t queue_size)
+{
+       return RTE_ALIGN_MUL_CEIL(sizeof(struct rte_vhost_inflight_desc_packed)
+                                 * queue_size + sizeof(uint64_t) +
+                                 sizeof(uint16_t) * 6 + sizeof(uint8_t) * 9,
+                                 INFLIGHT_ALIGNMENT);
+}
+
+static int
+vhost_user_get_inflight_fd(struct virtio_net **pdev,
+                          VhostUserMsg *msg,
+                          int main_fd __rte_unused)
+{
+       struct rte_vhost_inflight_info_packed *inflight_packed;
+       uint64_t pervq_inflight_size, mmap_size;
+       uint16_t num_queues, queue_size;
+       struct virtio_net *dev = *pdev;
+       int fd, i, j;
+       void *addr;
+
+       if (msg->size != sizeof(msg->payload.inflight)) {
+               RTE_LOG(ERR, VHOST_CONFIG,
+                       "invalid get_inflight_fd message size is %d\n",
+                       msg->size);
+               return RTE_VHOST_MSG_RESULT_ERR;
+       }
+
+       if (dev->inflight_info == NULL) {
+               dev->inflight_info = calloc(1,
+                                           sizeof(struct inflight_mem_info));
+               if (!dev->inflight_info) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "failed to alloc dev inflight area\n");
+                       return RTE_VHOST_MSG_RESULT_ERR;
+               }
+       }
+
+       num_queues = msg->payload.inflight.num_queues;
+       queue_size = msg->payload.inflight.queue_size;
+
+       RTE_LOG(INFO, VHOST_CONFIG, "get_inflight_fd num_queues: %u\n",
+               msg->payload.inflight.num_queues);
+       RTE_LOG(INFO, VHOST_CONFIG, "get_inflight_fd queue_size: %u\n",
+               msg->payload.inflight.queue_size);
+
+       if (vq_is_packed(dev))
+               pervq_inflight_size = get_pervq_shm_size_packed(queue_size);
+       else
+               pervq_inflight_size = get_pervq_shm_size_split(queue_size);
+
+       mmap_size = num_queues * pervq_inflight_size;
+       addr = inflight_mem_alloc("vhost-inflight", mmap_size, &fd);
+       if (!addr) {
+               RTE_LOG(ERR, VHOST_CONFIG,
+                       "failed to alloc vhost inflight area\n");
+                       msg->payload.inflight.mmap_size = 0;
+               return RTE_VHOST_MSG_RESULT_ERR;
+       }
+       memset(addr, 0, mmap_size);
+
+       dev->inflight_info->addr = addr;
+       dev->inflight_info->size = msg->payload.inflight.mmap_size = mmap_size;
+       dev->inflight_info->fd = msg->fds[0] = fd;
+       msg->payload.inflight.mmap_offset = 0;
+       msg->fd_num = 1;
+
+       if (vq_is_packed(dev)) {
+               for (i = 0; i < num_queues; i++) {
+                       inflight_packed =
+                               (struct rte_vhost_inflight_info_packed *)addr;
+                       inflight_packed->used_wrap_counter = 1;
+                       inflight_packed->old_used_wrap_counter = 1;
+                       for (j = 0; j < queue_size; j++)
+                               inflight_packed->desc[j].next = j + 1;
+                       addr = (void *)((char *)addr + pervq_inflight_size);
+               }
+       }
+
+       RTE_LOG(INFO, VHOST_CONFIG,
+               "send inflight mmap_size: %"PRIu64"\n",
+               msg->payload.inflight.mmap_size);
+       RTE_LOG(INFO, VHOST_CONFIG,
+               "send inflight mmap_offset: %"PRIu64"\n",
+               msg->payload.inflight.mmap_offset);
+       RTE_LOG(INFO, VHOST_CONFIG,
+               "send inflight fd: %d\n", msg->fds[0]);
+
+       return RTE_VHOST_MSG_RESULT_REPLY;
+}
+
+static int
+vhost_user_set_inflight_fd(struct virtio_net **pdev, VhostUserMsg *msg,
+                          int main_fd __rte_unused)
+{
+       uint64_t mmap_size, mmap_offset;
+       uint16_t num_queues, queue_size;
+       struct virtio_net *dev = *pdev;
+       uint32_t pervq_inflight_size;
+       struct vhost_virtqueue *vq;
+       void *addr;
+       int fd, i;
+
+       fd = msg->fds[0];
+       if (msg->size != sizeof(msg->payload.inflight) || fd < 0) {
+               RTE_LOG(ERR, VHOST_CONFIG,
+                       "invalid set_inflight_fd message size is %d,fd is %d\n",
+                       msg->size, fd);
+               return RTE_VHOST_MSG_RESULT_ERR;
+       }
+
+       mmap_size = msg->payload.inflight.mmap_size;
+       mmap_offset = msg->payload.inflight.mmap_offset;
+       num_queues = msg->payload.inflight.num_queues;
+       queue_size = msg->payload.inflight.queue_size;
+
+       if (vq_is_packed(dev))
+               pervq_inflight_size = get_pervq_shm_size_packed(queue_size);
+       else
+               pervq_inflight_size = get_pervq_shm_size_split(queue_size);
+
+       RTE_LOG(INFO, VHOST_CONFIG,
+               "set_inflight_fd mmap_size: %"PRIu64"\n", mmap_size);
+       RTE_LOG(INFO, VHOST_CONFIG,
+               "set_inflight_fd mmap_offset: %"PRIu64"\n", mmap_offset);
+       RTE_LOG(INFO, VHOST_CONFIG,
+               "set_inflight_fd num_queues: %u\n", num_queues);
+       RTE_LOG(INFO, VHOST_CONFIG,
+               "set_inflight_fd queue_size: %u\n", queue_size);
+       RTE_LOG(INFO, VHOST_CONFIG,
+               "set_inflight_fd fd: %d\n", fd);
+       RTE_LOG(INFO, VHOST_CONFIG,
+               "set_inflight_fd pervq_inflight_size: %d\n",
+               pervq_inflight_size);
+
+       if (!dev->inflight_info) {
+               dev->inflight_info = calloc(1,
+                                           sizeof(struct inflight_mem_info));
+               if (dev->inflight_info == NULL) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "failed to alloc dev inflight area\n");
+                       return RTE_VHOST_MSG_RESULT_ERR;
+               }
+       }
+
+       if (dev->inflight_info->addr)
+               munmap(dev->inflight_info->addr, dev->inflight_info->size);
+
+       addr = mmap(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+                   fd, mmap_offset);
+       if (addr == MAP_FAILED) {
+               RTE_LOG(ERR, VHOST_CONFIG, "failed to mmap share memory.\n");
+               return RTE_VHOST_MSG_RESULT_ERR;
+       }
+
+       if (dev->inflight_info->fd)
+               close(dev->inflight_info->fd);
+
+       dev->inflight_info->fd = fd;
+       dev->inflight_info->addr = addr;
+       dev->inflight_info->size = mmap_size;
+
+       for (i = 0; i < num_queues; i++) {
+               vq = dev->virtqueue[i];
+               if (vq_is_packed(dev)) {
+                       vq->inflight_packed = addr;
+                       vq->inflight_packed->desc_num = queue_size;
+               } else {
+                       vq->inflight_split = addr;
+                       vq->inflight_split->desc_num = queue_size;
+               }
+               addr = (void *)((char *)addr + pervq_inflight_size);
+       }
+
+       return RTE_VHOST_MSG_RESULT_OK;
+}
+
 static int
 vhost_user_set_vring_call(struct virtio_net **pdev, struct VhostUserMsg *msg,
                        int main_fd __rte_unused)
@@ -1186,6 +1564,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;
@@ -1207,6 +1588,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");
@@ -1214,6 +1598,191 @@ static int vhost_user_set_vring_err(struct virtio_net **pdev __rte_unused,
        return RTE_VHOST_MSG_RESULT_OK;
 }
 
+static int
+resubmit_desc_compare(const void *a, const void *b)
+{
+       const struct rte_vhost_resubmit_desc *desc0 = a;
+       const struct rte_vhost_resubmit_desc *desc1 = b;
+
+       if (desc1->counter > desc0->counter)
+               return 1;
+
+       return -1;
+}
+
+static int
+vhost_check_queue_inflights_split(struct virtio_net *dev,
+                                 struct vhost_virtqueue *vq)
+{
+       uint16_t i;
+       uint16_t resubmit_num = 0, last_io, num;
+       struct vring_used *used = vq->used;
+       struct rte_vhost_resubmit_info *resubmit;
+       struct rte_vhost_inflight_info_split *inflight_split;
+
+       if (!(dev->protocol_features &
+           (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)))
+               return RTE_VHOST_MSG_RESULT_OK;
+
+       if ((!vq->inflight_split))
+               return RTE_VHOST_MSG_RESULT_ERR;
+
+       if (!vq->inflight_split->version) {
+               vq->inflight_split->version = INFLIGHT_VERSION;
+               return RTE_VHOST_MSG_RESULT_OK;
+       }
+
+       if (vq->resubmit_inflight)
+               return RTE_VHOST_MSG_RESULT_OK;
+
+       inflight_split = vq->inflight_split;
+       vq->global_counter = 0;
+       last_io = inflight_split->last_inflight_io;
+
+       if (inflight_split->used_idx != used->idx) {
+               inflight_split->desc[last_io].inflight = 0;
+               rte_smp_mb();
+               inflight_split->used_idx = used->idx;
+       }
+
+       for (i = 0; i < inflight_split->desc_num; i++) {
+               if (inflight_split->desc[i].inflight == 1)
+                       resubmit_num++;
+       }
+
+       vq->last_avail_idx += resubmit_num;
+
+       if (resubmit_num) {
+               resubmit  = calloc(1, sizeof(struct rte_vhost_resubmit_info));
+               if (!resubmit) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "failed to allocate memory for resubmit info.\n");
+                       return RTE_VHOST_MSG_RESULT_ERR;
+               }
+
+               resubmit->resubmit_list = calloc(resubmit_num,
+                       sizeof(struct rte_vhost_resubmit_desc));
+               if (!resubmit->resubmit_list) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "failed to allocate memory for inflight desc.\n");
+                       free(resubmit);
+                       return RTE_VHOST_MSG_RESULT_ERR;
+               }
+
+               num = 0;
+               for (i = 0; i < vq->inflight_split->desc_num; i++) {
+                       if (vq->inflight_split->desc[i].inflight == 1) {
+                               resubmit->resubmit_list[num].index = i;
+                               resubmit->resubmit_list[num].counter =
+                                       inflight_split->desc[i].counter;
+                               num++;
+                       }
+               }
+               resubmit->resubmit_num = num;
+
+               if (resubmit->resubmit_num > 1)
+                       qsort(resubmit->resubmit_list, resubmit->resubmit_num,
+                             sizeof(struct rte_vhost_resubmit_desc),
+                             resubmit_desc_compare);
+
+               vq->global_counter = resubmit->resubmit_list[0].counter + 1;
+               vq->resubmit_inflight = resubmit;
+       }
+
+       return RTE_VHOST_MSG_RESULT_OK;
+}
+
+static int
+vhost_check_queue_inflights_packed(struct virtio_net *dev,
+                                  struct vhost_virtqueue *vq)
+{
+       uint16_t i;
+       uint16_t resubmit_num = 0, old_used_idx, num;
+       struct rte_vhost_resubmit_info *resubmit;
+       struct rte_vhost_inflight_info_packed *inflight_packed;
+
+       if (!(dev->protocol_features &
+           (1ULL << VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)))
+               return RTE_VHOST_MSG_RESULT_OK;
+
+       if ((!vq->inflight_packed))
+               return RTE_VHOST_MSG_RESULT_ERR;
+
+       if (!vq->inflight_packed->version) {
+               vq->inflight_packed->version = INFLIGHT_VERSION;
+               return RTE_VHOST_MSG_RESULT_OK;
+       }
+
+       if (vq->resubmit_inflight)
+               return RTE_VHOST_MSG_RESULT_OK;
+
+       inflight_packed = vq->inflight_packed;
+       vq->global_counter = 0;
+       old_used_idx = inflight_packed->old_used_idx;
+
+       if (inflight_packed->used_idx != old_used_idx) {
+               if (inflight_packed->desc[old_used_idx].inflight == 0) {
+                       inflight_packed->old_used_idx =
+                               inflight_packed->used_idx;
+                       inflight_packed->old_used_wrap_counter =
+                               inflight_packed->used_wrap_counter;
+                       inflight_packed->old_free_head =
+                               inflight_packed->free_head;
+               } else {
+                       inflight_packed->used_idx =
+                               inflight_packed->old_used_idx;
+                       inflight_packed->used_wrap_counter =
+                               inflight_packed->old_used_wrap_counter;
+                       inflight_packed->free_head =
+                               inflight_packed->old_free_head;
+               }
+       }
+
+       for (i = 0; i < inflight_packed->desc_num; i++) {
+               if (inflight_packed->desc[i].inflight == 1)
+                       resubmit_num++;
+       }
+
+       if (resubmit_num) {
+               resubmit = calloc(1, sizeof(struct rte_vhost_resubmit_info));
+               if (resubmit == NULL) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "failed to allocate memory for resubmit info.\n");
+                       return RTE_VHOST_MSG_RESULT_ERR;
+               }
+
+               resubmit->resubmit_list = calloc(resubmit_num,
+                       sizeof(struct rte_vhost_resubmit_desc));
+               if (resubmit->resubmit_list == NULL) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "failed to allocate memory for resubmit desc.\n");
+                       free(resubmit);
+                       return RTE_VHOST_MSG_RESULT_ERR;
+               }
+
+               num = 0;
+               for (i = 0; i < inflight_packed->desc_num; i++) {
+                       if (vq->inflight_packed->desc[i].inflight == 1) {
+                               resubmit->resubmit_list[num].index = i;
+                               resubmit->resubmit_list[num].counter =
+                                       inflight_packed->desc[i].counter;
+                               num++;
+                       }
+               }
+               resubmit->resubmit_num = num;
+
+               if (resubmit->resubmit_num > 1)
+                       qsort(resubmit->resubmit_list, resubmit->resubmit_num,
+                             sizeof(struct rte_vhost_resubmit_desc),
+                             resubmit_desc_compare);
+
+               vq->global_counter = resubmit->resubmit_list[0].counter + 1;
+               vq->resubmit_inflight = resubmit;
+       }
+
+       return RTE_VHOST_MSG_RESULT_OK;
+}
+
 static int
 vhost_user_set_vring_kick(struct virtio_net **pdev, struct VhostUserMsg *msg,
                        int main_fd __rte_unused)
@@ -1222,6 +1791,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;
@@ -1255,6 +1827,20 @@ vhost_user_set_vring_kick(struct virtio_net **pdev, struct VhostUserMsg *msg,
                close(vq->kickfd);
        vq->kickfd = file.fd;
 
+       if (vq_is_packed(dev)) {
+               if (vhost_check_queue_inflights_packed(dev, vq)) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "failed to inflights for vq: %d\n", file.index);
+                       return RTE_VHOST_MSG_RESULT_ERR;
+               }
+       } else {
+               if (vhost_check_queue_inflights_split(dev, vq)) {
+                       RTE_LOG(ERR, VHOST_CONFIG,
+                               "failed to inflights for vq: %d\n", file.index);
+                       return RTE_VHOST_MSG_RESULT_ERR;
+               }
+       }
+
        return RTE_VHOST_MSG_RESULT_OK;
 }
 
@@ -1278,6 +1864,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);
 
@@ -1353,6 +1942,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);
@@ -1383,6 +1975,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);
 
@@ -1411,6 +2006,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) {
@@ -1437,6 +2035,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;
@@ -1500,6 +2101,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");
 
@@ -1523,6 +2127,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]);
@@ -1550,6 +2157,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",
@@ -1570,6 +2181,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",
@@ -1655,6 +2269,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;
@@ -1701,6 +2318,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) {
@@ -1736,6 +2356,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");
@@ -1752,6 +2375,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);
@@ -1795,9 +2421,10 @@ static vhost_message_handler_t vhost_message_handlers[VHOST_USER_MAX] = {
        [VHOST_USER_POSTCOPY_ADVISE] = vhost_user_set_postcopy_advise,
        [VHOST_USER_POSTCOPY_LISTEN] = vhost_user_set_postcopy_listen,
        [VHOST_USER_POSTCOPY_END] = vhost_user_postcopy_end,
+       [VHOST_USER_GET_INFLIGHT_FD] = vhost_user_get_inflight_fd,
+       [VHOST_USER_SET_INFLIGHT_FD] = vhost_user_set_inflight_fd,
 };
 
-
 /* return bytes# of read on success or negative val on failure. */
 static int
 read_vhost_message(int sockfd, struct VhostUserMsg *msg)
@@ -2104,6 +2731,7 @@ skip_to_post_handle:
        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;
        }