From 7315a38a024e678963deb27b46e1c97956a62291 Mon Sep 17 00:00:00 2001 From: Pascal Scholz Date: Thu, 26 Mar 2026 09:17:44 +0100 Subject: [PATCH] vmm: Validate PCI device ID Validate the PCI device ID are within range and not using the reserved value. We need this option to ensure that invalid device IDs received via an API call result in an error as soon as possible. In this case, this would be after deserialization. On this code path, validation via `parse` is skipped and must be invoked by calling `validate`. Signed-off-by: Pascal Scholz On-behalf-of: SAP pascal.scholz@sap.com Signed-off-by: Rob Bradford --- vmm/src/config.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/vmm/src/config.rs b/vmm/src/config.rs index 5e1414aa1..72f0f6d47 100644 --- a/vmm/src/config.rs +++ b/vmm/src/config.rs @@ -17,6 +17,7 @@ use log::{debug, warn}; use option_parser::{ ByteSized, IntegerList, OptionParser, OptionParserError, StringList, Toggle, Tuple, }; +use pci::NUM_DEVICE_IDS; use serde::{Deserialize, Serialize}; use thiserror::Error; use virtio_bindings::virtio_blk::VIRTIO_BLK_ID_BYTES; @@ -402,6 +403,13 @@ pub enum ValidationError { /// Invalid NUMA Configuration #[error("NUMA Configuration is invalid")] InvalidNumaConfig(String), + /// The supplied PCI ID was greater then the max. supported number + /// of devices per Bus + #[error("Given PCI device ID ({0}) is out of the supported range of 0..{NUM_DEVICE_IDS}")] + InvalidPciDeviceId(u8), + /// The supplied PCI ID is reserved + #[error("Given PCI device ID ({0}) is reserved")] + ReservedPciDeviceId(u8), } type ValidationResult = std::result::Result; @@ -414,6 +422,21 @@ pub fn add_to_config(items: &mut Option>, item: T) { } } +/// Check that the PCI device supplied is neither out of range nor does +/// it use any reserved device ID. +fn validate_pci_device_id(device_id: u8) -> ValidationResult<()> { + if device_id >= pci::NUM_DEVICE_IDS { + // Check the given ID is not out of range + return Err(ValidationError::InvalidPciDeviceId(device_id)); + } else if device_id == pci::PCI_ROOT_DEVICE_ID { + // Check the ID isn't any reserved one. Currently, only the device ID + // for the root device is reserved. + return Err(ValidationError::ReservedPciDeviceId(device_id)); + } + + Ok(()) +} + pub type Result = result::Result; pub struct VmParams<'a> { @@ -1243,6 +1266,10 @@ impl PciDeviceCommonConfig { } } + if let Some(device_id) = self.pci_device_id { + validate_pci_device_id(device_id)?; + } + Ok(()) } } @@ -5643,7 +5670,7 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" }]); still_valid_config.validate().unwrap(); - let mut still_valid_config = valid_config; + let mut still_valid_config = valid_config.clone(); // SAFETY: Safe as the file was just opened let fd1 = unsafe { libc::dup(File::open("/dev/null").unwrap().as_raw_fd()) }; // SAFETY: Safe as the file was just opened @@ -5653,6 +5680,45 @@ id=\"{id}\",pci_segment={pci_segment},queue_sizes={queue_sizes}" still_valid_config.add_preserved_fds(vec![fd1, fd2]); } let _still_valid_config = still_valid_config.clone(); + + // Valid BDF test + let mut still_valid_config = valid_config.clone(); + still_valid_config.disks = Some(vec![DiskConfig { + pci_common: PciDeviceCommonConfig { + pci_device_id: Some(8), + ..Default::default() + }, + ..disk_fixture() + }]); + still_valid_config.validate().unwrap(); + // Invalid BDF - Same ID as Root device + let mut invalid_config = valid_config.clone(); + invalid_config.disks = Some(vec![DiskConfig { + pci_common: PciDeviceCommonConfig { + pci_device_id: Some(pci::PCI_ROOT_DEVICE_ID), + ..Default::default() + }, + ..disk_fixture() + }]); + assert_eq!( + invalid_config.validate(), + Err(ValidationError::ReservedPciDeviceId( + pci::PCI_ROOT_DEVICE_ID + )) + ); + // Invalid BDF - Out of range + let mut invalid_config = valid_config.clone(); + invalid_config.disks = Some(vec![DiskConfig { + pci_common: PciDeviceCommonConfig { + pci_device_id: Some(pci::NUM_DEVICE_IDS + 1), + ..Default::default() + }, + ..disk_fixture() + }]); + assert_eq!( + invalid_config.validate(), + Err(ValidationError::InvalidPciDeviceId(pci::NUM_DEVICE_IDS + 1)) + ); } #[test] fn test_landlock_parsing() -> Result<()> {