From 5b53f4202d183c2f890d651b6281b66c1e6cd9fe Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 8 Jul 2026 12:12:13 -0700 Subject: [PATCH] vmm: Reject removal of already removed devices A hot-unplug leaves the PCI node in the device tree until the guest acknowledges the ejection, but VmConfig drops the device entry immediately. Move the config removal into DeviceManager::remove_device() and fail when it returns false, so a second remove-device request cannot reuse the stale device-tree node. Assisted-by: OpenAI:Codex-GPT-5 Signed-off-by: Rob Bradford --- vmm/src/device_manager.rs | 50 ++++++++++++++++++++++++--------------- vmm/src/vm.rs | 4 ---- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index ec58b86bd..416132e62 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -5023,20 +5023,24 @@ impl DeviceManager { match device_type { VirtioDeviceType::Net => { let mut config = self.config.lock().unwrap(); - let nets = config.net.as_deref_mut().unwrap(); - let net_dev_cfg = nets - .iter_mut() - .find(|net| net.pci_common.id.as_deref() == Some(id)) - // unwrap: the device could not have been removed without an ID - .unwrap(); - let fds = net_dev_cfg.fds.take().unwrap_or(Vec::new()); - - debug!("Closing preserved FDs from virtio-net device: id={id}, fds={fds:?}"); - for fd in fds { - config.preserved_fds.as_mut().unwrap().remove(&fd); - // SAFETY: We are closing the only remaining instance of this FD. - unsafe { - libc::close(fd); + if let Some(fds) = config + .net + .as_deref_mut() + .and_then(|nets| { + nets.iter_mut() + .find(|net| net.pci_common.id.as_deref() == Some(id)) + }) + .map(|net_dev_cfg| net_dev_cfg.fds.take().unwrap_or_default()) + { + debug!( + "Closing preserved FDs from virtio-net device: id={id}, fds={fds:?}" + ); + for fd in fds { + config.preserved_fds.as_mut().unwrap().remove(&fd); + // SAFETY: We are closing the only remaining instance of this FD. + unsafe { + libc::close(fd); + } } } } @@ -5050,11 +5054,15 @@ impl DeviceManager { // Cleanup externally-provided VFIO cdev FDs: remove the preserved // original from VmConfig and close it. let mut config = self.config.lock().unwrap(); - if let Some(devices) = config.devices.as_deref_mut() - && let Some(device_cfg) = devices - .iter_mut() - .find(|d| d.pci_common.id.as_deref() == Some(id)) - && let Some(fd) = device_cfg.fd.take() + if let Some(fd) = config + .devices + .as_deref_mut() + .and_then(|devices| { + devices + .iter_mut() + .find(|d| d.pci_common.id.as_deref() == Some(id)) + }) + .and_then(|device_cfg| device_cfg.fd.take()) { debug!("Closing preserved FD from VFIO device: id={id}, fd={fd}"); let fd_removed = config.preserved_fds.as_mut().unwrap().remove(&fd); @@ -5069,6 +5077,10 @@ impl DeviceManager { } } + if !self.config.lock().unwrap().remove_device(id) { + return Err(DeviceManagerError::UnknownDeviceId(id.to_string())); + } + // Update the PCID bitmap self.pci_segments[pci_segment_id as usize].pci_devices_down |= 1 << pci_device_bdf.device(); diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index effcd988d..3bcb5a34d 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -2236,10 +2236,6 @@ impl Vm { .remove_device(id) .map_err(Error::DeviceManager)?; - // Update VmConfig by removing the device. This is important to - // ensure the device would not be created in case of a reboot. - self.config.lock().unwrap().remove_device(id); - self.device_manager .lock() .unwrap()