From cdfedfaab24e51f034dd7fd37885d0188c07eaa5 Mon Sep 17 00:00:00 2001 From: Max Makarov Date: Thu, 16 Apr 2026 21:05:27 +0000 Subject: [PATCH] vmm: device_manager: reject duplicate socket in add_user_device Calling vm.add-user-device a second time with a socket path already in use makes the VMM thread block indefinitely inside vfio_user::Client::new(). libvfio-user servers (SPDK, the reference libvfio-user daemon) accept a single active client per socket, so the second connect(2) succeeds at the OS level but the handshake recvmsg(2) waits for a response that never arrives. All subsequent API requests queue behind the stuck VMM event loop and also hang (vm.info, vmm.ping, vm.remove-device). The VM itself keeps running on vcpu threads, making the symptom confusing: the guest looks healthy, only the API is unreachable. This is easy to hit from management software that uses an idempotent reconcile / ensure pattern for user devices. Reject the call up-front when another user_device already has the same socket path, returning an HTTP 500 with a descriptive UserDeviceSocketInUse error in milliseconds instead of hanging. Signed-off-by: Max Makarov --- vmm/src/device_manager.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 2ea8efe35..97e774f98 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -657,6 +657,10 @@ pub enum DeviceManagerError { #[error("Invalid identifier: {0}")] InvalidIdentifier(String), + /// vfio-user socket path already in use by another user device. + #[error("vfio-user socket path already in use: {0:?}")] + UserDeviceSocketInUse(std::path::PathBuf), + /// Error activating virtio device #[error("Error activating virtio device")] VirtioActivate(#[source] ActivateError), @@ -4707,6 +4711,17 @@ impl DeviceManager { ) -> DeviceManagerResult { self.validate_identifier(&device_cfg.pci_common.id)?; + // Reject duplicate socket up-front: libvfio-user servers accept a + // single client, so a second Client::new() on the same socket blocks + // indefinitely in the handshake recvmsg() and hangs the VMM thread. + if let Some(existing) = &self.config.lock().unwrap().user_devices + && existing.iter().any(|d| d.socket == device_cfg.socket) + { + return Err(DeviceManagerError::UserDeviceSocketInUse( + device_cfg.socket.clone(), + )); + } + let (bdf, device_name) = self.add_vfio_user_device(device_cfg)?; // Update the PCIU bitmap