diff --git a/vm-virtio/src/checked_descriptor.rs b/vm-virtio/src/checked_descriptor.rs index 04665e010..800f85cc6 100644 --- a/vm-virtio/src/checked_descriptor.rs +++ b/vm-virtio/src/checked_descriptor.rs @@ -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, 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()); + } }