Skip to content

Commit af1d121

Browse files
committed
fix: BAR writes through PCI configuration capability
PCI configuration capability allows a driver to access a BAR without mapping it in virtual address space. The driver issues reads/writes directly within the PCI configuration space (which should always be addressable either via MMIO or Port IO) which the device translates corresponding BAR accesses. The way this works is that the guests writes the offset and length of a BAR access within the capability structure and then reads/writes data using a 4-bytes dedicated array that also lives in the capability address space. We had a bug in the logic that handles writes where a guest would program a write of a certain length (L) and then try to perform a write using a buffer where buffer.len() < L. Our logic would then try to perform a write using the slice buffer[..L] which would cause Rust to panic with an out of range exception. Fix this by taking into account the buffer's length and using a slice with length min(L, buffer.len()). Signed-off-by: Babis Chalios <[email protected]>
1 parent 92e936d commit af1d121

File tree

1 file changed

+23
-4
lines changed
  • src/vmm/src/devices/virtio/transport/pci

1 file changed

+23
-4
lines changed

src/vmm/src/devices/virtio/transport/pci/device.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,7 @@ impl VirtioPciDevice {
599599
// offset is pointing to. So, do some check that the `length` has a meaningful value
600600
// and only use the part of the buffer we actually need.
601601
if len <= 4 {
602+
let len = len.min(data.len());
602603
self.write_bar(0, bar_offset as u64, &data[..len])
603604
} else {
604605
None
@@ -1400,7 +1401,7 @@ mod tests {
14001401
device.read_config_register(data_register)
14011402
}
14021403

1403-
fn cap_pci_cfg_write(device: &mut VirtioPciDevice, bar_offset: u32, length: u32, data: u32) {
1404+
fn cap_pci_cfg_write(device: &mut VirtioPciDevice, bar_offset: u32, length: u32, data: &[u8]) {
14041405
let pci_config_cap_offset = capabilities_start(device) as usize
14051406
+ 3 * (size_of::<VirtioPciCap>() + 2)
14061407
+ (size_of::<VirtioPciNotifyCap>() + 2);
@@ -1415,7 +1416,7 @@ mod tests {
14151416

14161417
device.write_config_register(offset_register, 0, bar_offset.as_slice());
14171418
device.write_config_register(length_register, 0, length.as_slice());
1418-
device.write_config_register(data_register, 0, data.as_slice());
1419+
device.write_config_register(data_register, 0, data);
14191420
}
14201421

14211422
#[test]
@@ -1437,7 +1438,12 @@ mod tests {
14371438
let len = 1u32;
14381439
let device_status = cap_pci_cfg_read(&mut locked_virtio_pci_device, bar_offset, len);
14391440
assert_eq!(device_status, 0);
1440-
cap_pci_cfg_write(&mut locked_virtio_pci_device, bar_offset, len, 0x42);
1441+
cap_pci_cfg_write(
1442+
&mut locked_virtio_pci_device,
1443+
bar_offset,
1444+
len,
1445+
0x42u32.as_slice(),
1446+
);
14411447
let device_status = cap_pci_cfg_read(&mut locked_virtio_pci_device, bar_offset, len);
14421448
assert_eq!(device_status, 0x42);
14431449

@@ -1447,11 +1453,24 @@ mod tests {
14471453
0
14481454
);
14491455
// writes out-of-bounds lengths should have no effect
1450-
cap_pci_cfg_write(&mut locked_virtio_pci_device, bar_offset, 8, 0x84);
1456+
cap_pci_cfg_write(
1457+
&mut locked_virtio_pci_device,
1458+
bar_offset,
1459+
8,
1460+
0x84u32.as_slice(),
1461+
);
14511462
assert_eq!(
14521463
cap_pci_cfg_read(&mut locked_virtio_pci_device, bar_offset, 1),
14531464
0x42
14541465
);
1466+
// Make sure that we handle properly from/to a BAR where the access length doesn't match
1467+
// what we've set in the capability's length
1468+
cap_pci_cfg_write(
1469+
&mut locked_virtio_pci_device,
1470+
bar_offset,
1471+
2,
1472+
0x42u8.as_slice(),
1473+
);
14551474
}
14561475

14571476
fn isr_status_read(device: &mut VirtioPciDevice) -> u32 {

0 commit comments

Comments
 (0)