vm-virtio: checked_descriptor: Add unit test for chain stop on invalid

Add yields_valid_prefix_then_stops_on_invalid which submits a two
descriptor chain where the first descriptor is valid and the second
overshoots guest memory. The test verifies CheckedDescriptorIter
yields the valid prefix, then terminates with the failed flag set.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-05-14 21:36:00 +02:00
committed by Rob Bradford
parent 3ced92702a
commit d345118854

View File

@@ -179,7 +179,7 @@ where
#[cfg(test)]
mod unit_tests {
use virtio_bindings::virtio_ring::VRING_DESC_F_WRITE;
use virtio_bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE};
use virtio_queue::{Queue, QueueT};
use vm_memory::bitmap::AtomicBitmap;
use vm_memory::{GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap};
@@ -212,6 +212,40 @@ mod unit_tests {
(mem, mem_atomic, queue)
}
/// Set up a chain of descriptors linked via VRING_DESC_F_NEXT in a
/// virtqueue backed by `mem_size` bytes of guest RAM. Each entry is
/// `(addr, len, flags)`; the helper adds the NEXT flag on every
/// descriptor except the last.
fn setup_vq_chain(
mem_size: usize,
descs: &[(u64, u32, u16)],
) -> (TestMmap, GuestMemoryAtomic<TestMmap>, Queue) {
let qsize = descs.len() as u16;
assert!(qsize >= 1);
let mem = TestMmap::from_ranges(&[(GuestAddress(0), mem_size)]).unwrap();
let guest_vq = GuestQ::new(GuestAddress(0x1_0000), &mem, qsize);
let queue = guest_vq.create_queue();
let last = descs.len() - 1;
for (i, &(addr, len, flags)) in descs.iter().enumerate() {
let (flags, next) = if i < last {
(
flags | u16::try_from(VRING_DESC_F_NEXT).unwrap(),
(i + 1) as u16,
)
} else {
(flags, 0)
};
guest_vq.dtable[i].set(addr, len, flags, next);
}
guest_vq.avail.ring[0].set(0);
guest_vq.avail.idx.set(1);
let mem_atomic = GuestMemoryAtomic::new(mem.clone());
(mem, mem_atomic, queue)
}
#[test]
fn yields_valid_single_descriptor() {
let (_mem, mem_atomic, mut queue) =
@@ -251,4 +285,19 @@ mod unit_tests {
.expect("zero length descriptor must pass through");
assert_eq!(desc.len(), 0);
}
#[test]
fn yields_valid_prefix_then_err_on_invalid() {
let (_mem, mem_atomic, mut queue) =
setup_vq_chain(128 * 1024, &[(0x4000, 256, 0), (0x8000, 1 << 30, 0)]);
let mem_guard = mem_atomic.memory();
let mut chain = queue.pop_descriptor_chain(mem_guard).unwrap();
let mut it = chain.checked_iter(None);
let first = it.next().unwrap().expect("first descriptor must be Ok");
assert_eq!(first.addr().0, 0x4000);
assert_eq!(first.len(), 256);
let second = it.next().expect("second must yield an item");
second.unwrap_err();
assert!(it.next().is_none());
}
}