Skip to content

Commit efccd18

Browse files
committed
fix(virtio-pci): check guest values for MSI-X vector
We should check that the MSI-X vector that a guest assigns to a VirtIO queue or configuration is a valid one. If it is a value bigger than the available vectors allocated for the device, it should not update the vector field and mark the corresponding vector to the NO_VECTOR value. Also, add checks in the MSI implementation for VirtioInterrupt trait, for making sure that we don't try to interact with out-of-range interrupt vectors. Signed-off-by: Babis Chalios <[email protected]>
1 parent eab2b40 commit efccd18

File tree

2 files changed

+42
-19
lines changed

2 files changed

+42
-19
lines changed

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

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use vm_memory::GuestAddress;
1616

1717
use crate::devices::virtio::device::VirtioDevice;
1818
use crate::devices::virtio::queue::Queue;
19+
use crate::devices::virtio::transport::pci::device::VIRTQ_MSI_NO_VECTOR;
1920
use crate::logger::{debug, error, info, trace, warn};
2021
pub const VIRTIO_PCI_COMMON_CONFIG_ID: &str = "virtio_pci_common_config";
2122

@@ -242,7 +243,7 @@ impl VirtioPciCommonConfig {
242243
.unwrap()
243244
.get(self.queue_select as usize)
244245
.copied()
245-
.unwrap_or(0xffff),
246+
.unwrap_or(VIRTQ_MSI_NO_VECTOR),
246247
0x1c => u16::from(self.with_queue(queues, |q| q.ready).unwrap_or(false)),
247248
0x1e => self.queue_select, // notify_off
248249
_ => {
@@ -255,19 +256,36 @@ impl VirtioPciCommonConfig {
255256
fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut [Queue]) {
256257
debug!("write_common_config_word: offset 0x{:x}", offset);
257258
match offset {
258-
0x10 => self.msix_config.store(value, Ordering::Release),
259+
0x10 => {
260+
// Make sure that the guest doesn't select an invalid vector. We are offering
261+
// `num_queues + 1` vectors (plus one for configuration updates). If an invalid
262+
// vector has been selected, we just store the `NO_VECTOR` value.
263+
let mut msix_queues = self.msix_queues.lock().expect("Poisoned lock");
264+
let nr_vectors = msix_queues.len() + 1;
265+
266+
if (value as usize) < nr_vectors {
267+
self.msix_config.store(value, Ordering::Release);
268+
} else {
269+
self.msix_config
270+
.store(VIRTQ_MSI_NO_VECTOR, Ordering::Release);
271+
}
272+
}
259273
0x16 => self.queue_select = value,
260274
0x18 => self.with_queue_mut(queues, |q| q.size = value),
261275
0x1a => {
276+
let mut msix_queues = self.msix_queues.lock().expect("Poisoned lock");
277+
let nr_vectors = msix_queues.len() + 1;
262278
// Make sure that `queue_select` points to a valid queue. If not, we won't do
263279
// anything here and subsequent reads at 0x1a will return `NO_VECTOR`.
264-
if let Some(msix_queue) = self
265-
.msix_queues
266-
.lock()
267-
.unwrap()
268-
.get_mut(self.queue_select as usize)
269-
{
270-
*msix_queue = value;
280+
if let Some(queue) = msix_queues.get_mut(self.queue_select as usize) {
281+
// Make sure that the guest doesn't select an invalid vector. We are offering
282+
// `num_queues + 1` vectors (plus one for configuration updates). If an invalid
283+
// vector has been selected, we just store the `NO_VECTOR` value.
284+
if (value as usize) < nr_vectors {
285+
*queue = value;
286+
} else {
287+
*queue = VIRTQ_MSI_NO_VECTOR;
288+
}
271289
}
272290
}
273291
0x1c => self.with_queue_mut(queues, |q| {
@@ -446,8 +464,8 @@ mod tests {
446464
// Valid `queue_select` though should setup the corresponding MSI-X queue.
447465
regs.write(0x16, &[0x1, 0x0], dev.clone());
448466
assert_eq!(regs.queue_select, 1);
449-
regs.write(0x1a, &[0x12, 0x13], dev.clone());
467+
regs.write(0x1a, &[0x1, 0x0], dev.clone());
450468
regs.read(0x1a, &mut read_back, dev);
451-
assert_eq!(LittleEndian::read_u16(&read_back[..2]), 0x1312);
469+
assert_eq!(LittleEndian::read_u16(&read_back[..2]), 0x1);
452470
}
453471
}

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::any::Any;
1111
use std::cmp;
1212
use std::collections::HashMap;
1313
use std::fmt::{Debug, Formatter};
14-
use std::io::Write;
14+
use std::io::{ErrorKind, Write};
1515
use std::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, AtomicUsize, Ordering};
1616
use std::sync::{Arc, Barrier, Mutex};
1717

@@ -65,7 +65,7 @@ const VIRTIO_F_SR_IOV: u32 = 37;
6565
const VIRTIO_F_NOTIFICATION_DATA: u32 = 38;
6666

6767
/// Vector value used to disable MSI for a queue.
68-
const VIRTQ_MSI_NO_VECTOR: u16 = 0xffff;
68+
pub const VIRTQ_MSI_NO_VECTOR: u16 = 0xffff;
6969

7070
/// BAR index we are using for VirtIO configuration
7171
const VIRTIO_BAR_INDEX: u8 = 0;
@@ -765,9 +765,12 @@ impl VirtioInterrupt for VirtioInterruptMsix {
765765
fn trigger(&self, int_type: VirtioInterruptType) -> std::result::Result<(), std::io::Error> {
766766
let vector = match int_type {
767767
VirtioInterruptType::Config => self.config_vector.load(Ordering::Acquire),
768-
VirtioInterruptType::Queue(queue_index) => {
769-
self.queues_vectors.lock().unwrap()[queue_index as usize]
770-
}
768+
VirtioInterruptType::Queue(queue_index) => *self
769+
.queues_vectors
770+
.lock()
771+
.unwrap()
772+
.get(queue_index as usize)
773+
.ok_or(ErrorKind::InvalidInput)?,
771774
};
772775

773776
if vector == VIRTQ_MSI_NO_VECTOR {
@@ -793,9 +796,11 @@ impl VirtioInterrupt for VirtioInterruptMsix {
793796
fn notifier(&self, int_type: VirtioInterruptType) -> Option<&EventFd> {
794797
let vector = match int_type {
795798
VirtioInterruptType::Config => self.config_vector.load(Ordering::Acquire),
796-
VirtioInterruptType::Queue(queue_index) => {
797-
self.queues_vectors.lock().unwrap()[queue_index as usize]
798-
}
799+
VirtioInterruptType::Queue(queue_index) => *self
800+
.queues_vectors
801+
.lock()
802+
.unwrap()
803+
.get(queue_index as usize)?,
799804
};
800805

801806
self.interrupt_source_group

0 commit comments

Comments
 (0)