From 6bc46ba9c1c4d99fecbe563e9e8913fb1c24827d Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 9 Aug 2022 12:14:41 +0100 Subject: [PATCH] vmm: config: Reject VFIO devices with the same path By checking in the validation logic we get checking for both devices specified in the initial config but also hotplug too. Fixes: #4453 Signed-off-by: Rob Bradford --- vmm/src/config.rs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 8a74cb339..8e4c2d8b9 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -175,6 +175,8 @@ pub enum ValidationError { InvalidIdentifier(String), /// Placing the device behind a virtual IOMMU is not supported IommuNotSupported, + /// Duplicated device path (device added twice) + DuplicateDevicePath(String), } type ValidationResult = std::result::Result; @@ -270,6 +272,7 @@ impl fmt::Display for ValidationError { IommuNotSupported => { write!(f, "Device does not support being placed behind IOMMU") } + DuplicateDevicePath(p) => write!(f, "Duplicated device path: {}", p), } } } @@ -2464,7 +2467,14 @@ impl VmConfig { } if let Some(devices) = &self.devices { + let mut device_paths = BTreeSet::new(); for device in devices { + if !device_paths.insert(device.path.to_string_lossy()) { + return Err(ValidationError::DuplicateDevicePath( + device.path.to_string_lossy().to_string(), + )); + } + device.validate(self)?; self.iommu |= device.iommu; @@ -3657,7 +3667,7 @@ mod tests { Err(ValidationError::OnIommuSegment(1)) ); - let mut invalid_config = valid_config; + let mut invalid_config = valid_config.clone(); invalid_config.memory.shared = true; invalid_config.platform = Some(PlatformConfig { num_pci_segments: 16, @@ -3672,5 +3682,31 @@ mod tests { invalid_config.validate(), Err(ValidationError::IommuNotSupportedOnSegment(1)) ); + + let mut still_valid_config = valid_config.clone(); + still_valid_config.devices = Some(vec![ + DeviceConfig { + path: "/device1".into(), + ..Default::default() + }, + DeviceConfig { + path: "/device2".into(), + ..Default::default() + }, + ]); + assert!(still_valid_config.validate().is_ok()); + + let mut invalid_config = valid_config; + invalid_config.devices = Some(vec![ + DeviceConfig { + path: "/device1".into(), + ..Default::default() + }, + DeviceConfig { + path: "/device1".into(), + ..Default::default() + }, + ]); + assert!(invalid_config.validate().is_err()); } }