From feb1c4a2d67f28f7f113871fb5e7386bfe764f8b Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Sat, 23 May 2026 16:09:07 +0200 Subject: [PATCH] virtio-devices: Respect PCI CFG cap.length for BAR access The VIRTIO_PCI_CAP_PCI_CFG indirect access mechanism was ignoring the cap.length field written by the guest driver. PCI config register reads always produce a 4 byte buffer, so when a driver set cap.length to 1 for a byte wide access to device_status at common config offset 0x14, the VMM passed all 4 bytes to read_bar, dispatching to the dword handler which does not cover that offset. Use cap.length to determine the actual BAR access width per virtio spec 4.1.4.9.1. Also replace the unsafe transmute with the safe Le32::to_native() conversion. Signed-off-by: Anatol Belski --- virtio-devices/src/transport/pci_device.rs | 26 +++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/virtio-devices/src/transport/pci_device.rs b/virtio-devices/src/transport/pci_device.rs index 5b0953eae..3923eb226 100644 --- a/virtio-devices/src/transport/pci_device.rs +++ b/virtio-devices/src/transport/pci_device.rs @@ -221,6 +221,14 @@ impl VirtioPciCfgCap { ..Default::default() } } + + /// Return the BAR offset and clamped access length for a PCI CFG cap + /// indirect BAR access. + fn bar_access_params(&self, data_len: usize) -> (u64, usize) { + let bar_offset = self.cap.offset.to_native() as u64; + let cap_length = self.cap.length.to_native() as usize; + (bar_offset, cmp::min(cap_length, data_len)) + } } #[derive(Clone, Copy, Default)] @@ -771,10 +779,10 @@ impl VirtioPciDevice { .unwrap(); } } else { - let bar_offset: u32 = - // SAFETY: we know self.cap_pci_cfg_info.cap.cap.offset is 32bits long. - unsafe { std::mem::transmute(self.cap_pci_cfg_info.cap.cap.offset) }; - self.read_bar(0, bar_offset as u64, data); + let (bar_offset, access_len) = self.cap_pci_cfg_info.cap.bar_access_params(data_len); + if access_len > 0 { + self.read_bar(0, bar_offset, &mut data[..access_len]); + } } } @@ -792,10 +800,12 @@ impl VirtioPciDevice { right[..data_len].copy_from_slice(data); None } else { - let bar_offset: u32 = - // SAFETY: we know self.cap_pci_cfg_info.cap.cap.offset is 32bits long. - unsafe { std::mem::transmute(self.cap_pci_cfg_info.cap.cap.offset) }; - self.write_bar(0, bar_offset as u64, data) + let (bar_offset, access_len) = self.cap_pci_cfg_info.cap.bar_access_params(data_len); + if access_len > 0 { + self.write_bar(0, bar_offset, &data[..access_len]) + } else { + None + } } }