Each SHMEM_MAP request creates a separate RAM MemoryRegion that
consumes a KVM memory slot. Once all KVM slots are in use, processing
another SHMEM_MAP request would make the KVM memory listener fail while
registering the new region.

Shared memory mappings are filtered out of the vhost memory table
(vhost_section() skips TYPE_VIRTIO_SHARED_MEMORY_MAPPING regions), so
they never count against the negotiated vhost-user slot limit; only
the KVM slot budget applies. Reject the request with ENOSPC before
changing the memory topology when KVM has no free slot left.

Also validate each vhost memory table against the slot limit negotiated
with the backend before sending regions via SET_MEM_TABLE or
ADD_MEM_REG.

Fixes: b52e1896e764 ("vhost-user: Add VirtIO Shared Memory map request")
Signed-off-by: Feifan Qian <[email protected]>
---
Based-on: <[email protected]>
("vhost-user-gpu: Add blob resource and shared memory support" v4,
which filters TYPE_VIRTIO_SHARED_MEMORY_MAPPING regions out of
vhost_section())

v3:
- Rebased on Dorinda's series as suggested by Albert.
- SHMEM_MAP path now checks kvm_get_free_memslots() only; shmem
  mappings no longer reach SET_MEM_TABLE/ADD_MEM_REG, so the precheck
  no longer calls vhost_get_free_memslots() or uses reserved_memslots.
- Kept the set_mem_table check as a safety net for ADD_MEM_REG.

Tested with an ASan/UBSan x86_64 build on top of the base series.
qtest-x86_64/qos-test passed all 141 subtests.

 hw/virtio/vhost-user.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index fac02a1ffc..f737a05d07 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -1126,6 +1126,13 @@ static int vhost_user_set_mem_table(struct vhost_dev 
*dev,
             dev, VHOST_USER_PROTOCOL_F_CONFIGURE_MEM_SLOTS);
     int ret;

+    if (mem->nregions > u->user->memory_slots) {
+        error_report("vhost-user memory table has %u regions, "
+                     "but the backend supports only %d",
+                     mem->nregions, u->user->memory_slots);
+        return -ENOSPC;
+    }
+
     if (do_postcopy) {
         /*
          * Postcopy has enough differences that it's best done in it's own
@@ -1980,6 +1987,17 @@ vhost_user_backend_handle_shmem_map(struct vhost_dev 
*dev,
         }
     }

+    /*
+     * Each SHMEM mapping becomes a separate RAM MemoryRegion and thus
+     * consumes a KVM memory slot. Reject the request before changing the
+     * memory topology if no slot is left.
+     */
+    if (kvm_enabled() && !kvm_get_free_memslots()) {
+        error_report("No free KVM memory slots for shared memory mapping");
+        ret = -ENOSPC;
+        goto send_reply;
+    }
+
     /* Create VirtioSharedMemoryMapping object */
     VirtioSharedMemoryMapping *mapping = virtio_shared_memory_mapping_new(
         vu_mmap->shmid, fd, vu_mmap->fd_offset, vu_mmap->shm_offset,
--
2.43.0

Reply via email to