// Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright (C) 2020-2021 Alibaba Cloud. All rights reserved. // Copyright © 2019 Intel Corporation. // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD-3-Clause file. // // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause use std::mem::size_of; use std::num::Wrapping; use std::ops::Deref; use std::sync::atomic::{fence, Ordering}; use vm_memory::{Address, Bytes, GuestAddress, GuestMemory}; use crate::defs::{ DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR, VIRTQ_AVAIL_ELEMENT_SIZE, VIRTQ_AVAIL_RING_HEADER_SIZE, VIRTQ_AVAIL_RING_META_SIZE, VIRTQ_USED_ELEMENT_SIZE, VIRTQ_USED_RING_HEADER_SIZE, VIRTQ_USED_RING_META_SIZE, }; use crate::{ error, Descriptor, DescriptorChain, Error, QueueGuard, QueueOwnedT, QueueState, QueueT, VirtqUsedElem, }; use virtio_bindings::bindings::virtio_ring::VRING_USED_F_NO_NOTIFY; /// The maximum queue size as defined in the Virtio Spec. pub const MAX_QUEUE_SIZE: u16 = 32768; /// Struct to maintain information and manipulate a virtio queue. /// /// # Example /// /// ```rust /// use virtio_queue::{Queue, QueueOwnedT, QueueT}; /// use vm_memory::{Bytes, GuestAddress, GuestAddressSpace, GuestMemoryMmap}; /// /// let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); /// let mut queue = Queue::new(1024).unwrap(); /// /// // First, the driver sets up the queue; this set up is done via writes on the bus (PCI, MMIO). /// queue.set_size(8); /// queue.set_desc_table_address(Some(0x1000), None); /// queue.set_avail_ring_address(Some(0x2000), None); /// queue.set_used_ring_address(Some(0x3000), None); /// queue.set_event_idx(true); /// queue.set_ready(true); /// // The user should check if the queue is valid before starting to use it. /// assert!(queue.is_valid(&m)); /// /// // Here the driver would add entries in the available ring and then update the `idx` field of /// // the available ring (address = 0x2000 + 2). /// m.write_obj(3, GuestAddress(0x2002)); /// /// loop { /// queue.disable_notification(&m).unwrap(); /// /// // Consume entries from the available ring. /// while let Some(chain) = queue.iter(&m).unwrap().next() { /// // Process the descriptor chain, and then add an entry in the used ring and optionally /// // notify the driver. /// queue.add_used(&m, chain.head_index(), 0x100).unwrap(); /// /// if queue.needs_notification(&m).unwrap() { /// // Here we would notify the driver it has new entries in the used ring to consume. /// } /// } /// if !queue.enable_notification(&m).unwrap() { /// break; /// } /// } /// /// // We can reset the queue at some point. /// queue.reset(); /// // The queue should not be ready after reset. /// assert!(!queue.ready()); /// ``` #[derive(Debug, Default, PartialEq, Eq)] pub struct Queue { /// The maximum size in elements offered by the device. max_size: u16, /// Tail position of the available ring. next_avail: Wrapping, /// Head position of the used ring. next_used: Wrapping, /// VIRTIO_F_RING_EVENT_IDX negotiated. event_idx_enabled: bool, /// The number of descriptor chains placed in the used ring via `add_used` /// since the last time `needs_notification` was called on the associated queue. num_added: Wrapping, /// The queue size in elements the driver selected. size: u16, /// Indicates if the queue is finished with configuration. ready: bool, /// Guest physical address of the descriptor table. desc_table: GuestAddress, /// Guest physical address of the available ring. avail_ring: GuestAddress, /// Guest physical address of the used ring. used_ring: GuestAddress, } impl Queue { /// Equivalent of [`QueueT::set_size`] returning an error in case of invalid size. /// /// This should not be directly used, as the preferred method is part of the [`QueueT`] /// interface. This is a convenience function for implementing save/restore capabilities. pub fn try_set_size(&mut self, size: u16) -> Result<(), Error> { if size > self.max_size() || size == 0 || (size & (size - 1)) != 0 { return Err(Error::InvalidSize); } self.size = size; Ok(()) } /// Tries to set the descriptor table address. In case of an invalid value, the address is /// not updated. /// /// This should not be directly used, as the preferred method is /// [`QueueT::set_desc_table_address`]. This is a convenience function for implementing /// save/restore capabilities. pub fn try_set_desc_table_address(&mut self, desc_table: GuestAddress) -> Result<(), Error> { if desc_table.mask(0xf) != 0 { return Err(Error::InvalidDescTableAlign); } self.desc_table = desc_table; Ok(()) } /// Tries to update the available ring address. In case of an invalid value, the address is /// not updated. /// /// This should not be directly used, as the preferred method is /// [`QueueT::set_avail_ring_address`]. This is a convenience function for implementing /// save/restore capabilities. pub fn try_set_avail_ring_address(&mut self, avail_ring: GuestAddress) -> Result<(), Error> { if avail_ring.mask(0x1) != 0 { return Err(Error::InvalidAvailRingAlign); } self.avail_ring = avail_ring; Ok(()) } /// Tries to update the used ring address. In cae of an invalid value, the address is not /// updated. /// /// This should not be directly used, as the preferred method is /// [`QueueT::set_used_ring_address`]. This is a convenience function for implementing /// save/restore capabilities. pub fn try_set_used_ring_address(&mut self, used_ring: GuestAddress) -> Result<(), Error> { if used_ring.mask(0x3) != 0 { return Err(Error::InvalidUsedRingAlign); } self.used_ring = used_ring; Ok(()) } /// Returns the state of the `Queue`. /// /// This is useful for implementing save/restore capabilities. /// The state does not have support for serialization, but this can be /// added by VMMs locally through the use of a /// [remote type](https://serde.rs/remote-derive.html). /// /// Alternatively, a version aware and serializable/deserializable QueueState /// is available in the `virtio-queue-ser` crate. pub fn state(&self) -> QueueState { QueueState { max_size: self.max_size, next_avail: self.next_avail(), next_used: self.next_used(), event_idx_enabled: self.event_idx_enabled, size: self.size, ready: self.ready, desc_table: self.desc_table(), avail_ring: self.avail_ring(), used_ring: self.used_ring(), } } // Helper method that writes `val` to the `avail_event` field of the used ring, using // the provided ordering. fn set_avail_event( &self, mem: &M, val: u16, order: Ordering, ) -> Result<(), Error> { // This can not overflow an u64 since it is working with relatively small numbers compared // to u64::MAX. let avail_event_offset = VIRTQ_USED_RING_HEADER_SIZE + VIRTQ_USED_ELEMENT_SIZE * u64::from(self.size); let addr = self .used_ring .checked_add(avail_event_offset) .ok_or(Error::AddressOverflow)?; mem.store(u16::to_le(val), addr, order) .map_err(Error::GuestMemory) } // Set the value of the `flags` field of the used ring, applying the specified ordering. fn set_used_flags( &mut self, mem: &M, val: u16, order: Ordering, ) -> Result<(), Error> { mem.store(u16::to_le(val), self.used_ring, order) .map_err(Error::GuestMemory) } // Write the appropriate values to enable or disable notifications from the driver. // // Every access in this method uses `Relaxed` ordering because a fence is added by the caller // when appropriate. fn set_notification(&mut self, mem: &M, enable: bool) -> Result<(), Error> { if enable { if self.event_idx_enabled { // We call `set_avail_event` using the `next_avail` value, instead of reading // and using the current `avail_idx` to avoid missing notifications. More // details in `enable_notification`. self.set_avail_event(mem, self.next_avail.0, Ordering::Relaxed) } else { self.set_used_flags(mem, 0, Ordering::Relaxed) } } else if !self.event_idx_enabled { self.set_used_flags(mem, VRING_USED_F_NO_NOTIFY as u16, Ordering::Relaxed) } else { // Notifications are effectively disabled by default after triggering once when // `VIRTIO_F_EVENT_IDX` is negotiated, so we don't do anything in that case. Ok(()) } } // Return the value present in the used_event field of the avail ring. // // If the VIRTIO_F_EVENT_IDX feature bit is not negotiated, the flags field in the available // ring offers a crude mechanism for the driver to inform the device that it doesn’t want // interrupts when buffers are used. Otherwise virtq_avail.used_event is a more performant // alternative where the driver specifies how far the device can progress before interrupting. // // Neither of these interrupt suppression methods are reliable, as they are not synchronized // with the device, but they serve as useful optimizations. So we only ensure access to the // virtq_avail.used_event is atomic, but do not need to synchronize with other memory accesses. fn used_event(&self, mem: &M, order: Ordering) -> Result, Error> { // This can not overflow an u64 since it is working with relatively small numbers compared // to u64::MAX. let used_event_offset = VIRTQ_AVAIL_RING_HEADER_SIZE + u64::from(self.size) * VIRTQ_AVAIL_ELEMENT_SIZE; let used_event_addr = self .avail_ring .checked_add(used_event_offset) .ok_or(Error::AddressOverflow)?; mem.load(used_event_addr, order) .map(u16::from_le) .map(Wrapping) .map_err(Error::GuestMemory) } } impl<'a> QueueGuard<'a> for Queue { type G = &'a mut Self; } impl QueueT for Queue { fn new(max_size: u16) -> Result { // We need to check that the max size is a power of 2 because we're setting this as the // queue size, and the valid queue sizes are a power of 2 as per the specification. if max_size == 0 || max_size > MAX_QUEUE_SIZE || (max_size & (max_size - 1)) != 0 { return Err(Error::InvalidMaxSize); } Ok(Queue { max_size, size: max_size, ready: false, desc_table: GuestAddress(DEFAULT_DESC_TABLE_ADDR), avail_ring: GuestAddress(DEFAULT_AVAIL_RING_ADDR), used_ring: GuestAddress(DEFAULT_USED_RING_ADDR), next_avail: Wrapping(0), next_used: Wrapping(0), event_idx_enabled: false, num_added: Wrapping(0), }) } fn is_valid(&self, mem: &M) -> bool { let queue_size = self.size as u64; let desc_table = self.desc_table; // The multiplication can not overflow an u64 since we are multiplying an u16 with a // small number. let desc_table_size = size_of::() as u64 * queue_size; let avail_ring = self.avail_ring; // The operations below can not overflow an u64 since they're working with relatively small // numbers compared to u64::MAX. let avail_ring_size = VIRTQ_AVAIL_RING_META_SIZE + VIRTQ_AVAIL_ELEMENT_SIZE * queue_size; let used_ring = self.used_ring; let used_ring_size = VIRTQ_USED_RING_META_SIZE + VIRTQ_USED_ELEMENT_SIZE * queue_size; if !self.ready { error!("attempt to use virtio queue that is not marked ready"); false } else if desc_table .checked_add(desc_table_size) .map_or(true, |v| !mem.address_in_range(v)) { error!( "virtio queue descriptor table goes out of bounds: start:0x{:08x} size:0x{:08x}", desc_table.raw_value(), desc_table_size ); false } else if avail_ring .checked_add(avail_ring_size) .map_or(true, |v| !mem.address_in_range(v)) { error!( "virtio queue available ring goes out of bounds: start:0x{:08x} size:0x{:08x}", avail_ring.raw_value(), avail_ring_size ); false } else if used_ring .checked_add(used_ring_size) .map_or(true, |v| !mem.address_in_range(v)) { error!( "virtio queue used ring goes out of bounds: start:0x{:08x} size:0x{:08x}", used_ring.raw_value(), used_ring_size ); false } else { true } } fn reset(&mut self) { self.ready = false; self.size = self.max_size; self.desc_table = GuestAddress(DEFAULT_DESC_TABLE_ADDR); self.avail_ring = GuestAddress(DEFAULT_AVAIL_RING_ADDR); self.used_ring = GuestAddress(DEFAULT_USED_RING_ADDR); self.next_avail = Wrapping(0); self.next_used = Wrapping(0); self.num_added = Wrapping(0); self.event_idx_enabled = false; } fn lock(&mut self) -> ::G { self } fn max_size(&self) -> u16 { self.max_size } fn size(&self) -> u16 { self.size } fn set_size(&mut self, size: u16) { if self.try_set_size(size).is_err() { error!("virtio queue with invalid size: {}", size); } } fn ready(&self) -> bool { self.ready } fn set_ready(&mut self, ready: bool) { self.ready = ready; } fn set_desc_table_address(&mut self, low: Option, high: Option) { let low = low.unwrap_or(self.desc_table.0 as u32) as u64; let high = high.unwrap_or((self.desc_table.0 >> 32) as u32) as u64; let desc_table = GuestAddress((high << 32) | low); if self.try_set_desc_table_address(desc_table).is_err() { error!("virtio queue descriptor table breaks alignment constraints"); } } fn set_avail_ring_address(&mut self, low: Option, high: Option) { let low = low.unwrap_or(self.avail_ring.0 as u32) as u64; let high = high.unwrap_or((self.avail_ring.0 >> 32) as u32) as u64; let avail_ring = GuestAddress((high << 32) | low); if self.try_set_avail_ring_address(avail_ring).is_err() { error!("virtio queue available ring breaks alignment constraints"); } } fn set_used_ring_address(&mut self, low: Option, high: Option) { let low = low.unwrap_or(self.used_ring.0 as u32) as u64; let high = high.unwrap_or((self.used_ring.0 >> 32) as u32) as u64; let used_ring = GuestAddress((high << 32) | low); if self.try_set_used_ring_address(used_ring).is_err() { error!("virtio queue used ring breaks alignment constraints"); } } fn set_event_idx(&mut self, enabled: bool) { self.event_idx_enabled = enabled; } fn avail_idx(&self, mem: &M, order: Ordering) -> Result, Error> where M: GuestMemory + ?Sized, { let addr = self .avail_ring .checked_add(2) .ok_or(Error::AddressOverflow)?; mem.load(addr, order) .map(u16::from_le) .map(Wrapping) .map_err(Error::GuestMemory) } fn used_idx(&self, mem: &M, order: Ordering) -> Result, Error> { let addr = self .used_ring .checked_add(2) .ok_or(Error::AddressOverflow)?; mem.load(addr, order) .map(u16::from_le) .map(Wrapping) .map_err(Error::GuestMemory) } fn add_used( &mut self, mem: &M, head_index: u16, len: u32, ) -> Result<(), Error> { if head_index >= self.size { error!( "attempted to add out of bounds descriptor to used ring: {}", head_index ); return Err(Error::InvalidDescriptorIndex); } let next_used_index = u64::from(self.next_used.0 % self.size); // This can not overflow an u64 since it is working with relatively small numbers compared // to u64::MAX. let offset = VIRTQ_USED_RING_HEADER_SIZE + next_used_index * VIRTQ_USED_ELEMENT_SIZE; let addr = self .used_ring .checked_add(offset) .ok_or(Error::AddressOverflow)?; mem.write_obj(VirtqUsedElem::new(head_index.into(), len), addr) .map_err(Error::GuestMemory)?; self.next_used += Wrapping(1); self.num_added += Wrapping(1); mem.store( u16::to_le(self.next_used.0), self.used_ring .checked_add(2) .ok_or(Error::AddressOverflow)?, Ordering::Release, ) .map_err(Error::GuestMemory) } // TODO: Turn this into a doc comment/example. // With the current implementation, a common way of consuming entries from the available ring // while also leveraging notification suppression is to use a loop, for example: // // loop { // // We have to explicitly disable notifications if `VIRTIO_F_EVENT_IDX` has not been // // negotiated. // self.disable_notification()?; // // for chain in self.iter()? { // // Do something with each chain ... // // Let's assume we process all available chains here. // } // // // If `enable_notification` returns `true`, the driver has added more entries to the // // available ring. // if !self.enable_notification()? { // break; // } // } fn enable_notification(&mut self, mem: &M) -> Result { self.set_notification(mem, true)?; // Ensures the following read is not reordered before any previous write operation. fence(Ordering::SeqCst); // We double check here to avoid the situation where the available ring has been updated // just before we re-enabled notifications, and it's possible to miss one. We compare the // current `avail_idx` value to `self.next_avail` because it's where we stopped processing // entries. There are situations where we intentionally avoid processing everything in the // available ring (which will cause this method to return `true`), but in that case we'll // probably not re-enable notifications as we already know there are pending entries. self.avail_idx(mem, Ordering::Relaxed) .map(|idx| idx != self.next_avail) } fn disable_notification(&mut self, mem: &M) -> Result<(), Error> { self.set_notification(mem, false) } fn needs_notification(&mut self, mem: &M) -> Result { let used_idx = self.next_used; // Complete all the writes in add_used() before reading the event. fence(Ordering::SeqCst); // The VRING_AVAIL_F_NO_INTERRUPT flag isn't supported yet. // When the `EVENT_IDX` feature is negotiated, the driver writes into `used_event` // a value that's used by the device to determine whether a notification must // be submitted after adding a descriptor chain to the used ring. According to the // standard, the notification must be sent when `next_used == used_event + 1`, but // various device model implementations rely on an inequality instead, most likely // to also support use cases where a bunch of descriptor chains are added to the used // ring first, and only afterwards the `needs_notification` logic is called. For example, // the approach based on `num_added` below is taken from the Linux Kernel implementation // (i.e. https://elixir.bootlin.com/linux/v5.15.35/source/drivers/virtio/virtio_ring.c#L661) // The `old` variable below is used to determine the value of `next_used` from when // `needs_notification` was called last (each `needs_notification` call resets `num_added` // to zero, while each `add_used` called increments it by one). Then, the logic below // uses wrapped arithmetic to see whether `used_event` can be found between `old` and // `next_used` in the circular sequence space of the used ring. if self.event_idx_enabled { let used_event = self.used_event(mem, Ordering::Relaxed)?; let old = used_idx - self.num_added; self.num_added = Wrapping(0); return Ok(used_idx - used_event - Wrapping(1) < used_idx - old); } Ok(true) } fn next_avail(&self) -> u16 { self.next_avail.0 } fn set_next_avail(&mut self, next_avail: u16) { self.next_avail = Wrapping(next_avail); } fn next_used(&self) -> u16 { self.next_used.0 } fn set_next_used(&mut self, next_used: u16) { self.next_used = Wrapping(next_used); } fn desc_table(&self) -> u64 { self.desc_table.0 } fn avail_ring(&self) -> u64 { self.avail_ring.0 } fn used_ring(&self) -> u64 { self.used_ring.0 } fn event_idx_enabled(&self) -> bool { self.event_idx_enabled } fn pop_descriptor_chain(&mut self, mem: M) -> Option> where M: Clone + Deref, M::Target: GuestMemory, { // Default, iter-based impl. Will be subsequently improved. match self.iter(mem) { Ok(mut iter) => iter.next(), Err(e) => { error!("Iterator error {}", e); None } } } } impl QueueOwnedT for Queue { fn iter(&mut self, mem: M) -> Result, Error> where M: Deref, M::Target: GuestMemory, { // We're checking here that a reset did not happen without re-initializing the queue. // TODO: In the future we might want to also check that the other parameters in the // queue are valid. if !self.ready || self.avail_ring == GuestAddress(0) { return Err(Error::QueueNotReady); } self.avail_idx(mem.deref(), Ordering::Acquire) .map(move |idx| AvailIter::new(mem, idx, self))? } fn go_to_previous_position(&mut self) { self.next_avail -= Wrapping(1); } } /// Consuming iterator over all available descriptor chain heads in the queue. /// /// # Example /// /// ```rust /// # use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; /// # use virtio_queue::mock::MockSplitQueue; /// use virtio_queue::{Descriptor, Queue, QueueOwnedT}; /// use vm_memory::{GuestAddress, GuestMemoryMmap}; /// /// # fn populate_queue(m: &GuestMemoryMmap) -> Queue { /// # let vq = MockSplitQueue::new(m, 16); /// # let mut q: Queue = vq.create_queue().unwrap(); /// # /// # // The chains are (0, 1), (2, 3, 4) and (5, 6). /// # let mut descs = Vec::new(); /// # for i in 0..7 { /// # let flags = match i { /// # 1 | 6 => 0, /// # 2 | 5 => VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, /// # 4 => VRING_DESC_F_WRITE, /// # _ => VRING_DESC_F_NEXT, /// # }; /// # /// # descs.push(Descriptor::new((0x1000 * (i + 1)) as u64, 0x1000, flags as u16, i + 1)); /// # } /// # /// # vq.add_desc_chains(&descs, 0).unwrap(); /// # q /// # } /// let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); /// // Populate the queue with descriptor chains and update the available ring accordingly. /// let mut queue = populate_queue(m); /// let mut i = queue.iter(m).unwrap(); /// /// { /// let mut c = i.next().unwrap(); /// let _first_head_index = c.head_index(); /// // We should have two descriptors in the first chain. /// let _desc1 = c.next().unwrap(); /// let _desc2 = c.next().unwrap(); /// } /// /// { /// let c = i.next().unwrap(); /// let _second_head_index = c.head_index(); /// /// let mut iter = c.writable(); /// // We should have two writable descriptors in the second chain. /// let _desc1 = iter.next().unwrap(); /// let _desc2 = iter.next().unwrap(); /// } /// /// { /// let c = i.next().unwrap(); /// let _third_head_index = c.head_index(); /// /// let mut iter = c.readable(); /// // We should have one readable descriptor in the third chain. /// let _desc1 = iter.next().unwrap(); /// } /// // Let's go back one position in the available ring. /// i.go_to_previous_position(); /// // We should be able to access again the third descriptor chain. /// let c = i.next().unwrap(); /// let _third_head_index = c.head_index(); /// ``` #[derive(Debug)] pub struct AvailIter<'b, M> { mem: M, desc_table: GuestAddress, avail_ring: GuestAddress, queue_size: u16, last_index: Wrapping, next_avail: &'b mut Wrapping, } impl<'b, M> AvailIter<'b, M> where M: Deref, M::Target: GuestMemory, { /// Create a new instance of `AvailInter`. /// /// # Arguments /// * `mem` - the `GuestMemory` object that can be used to access the queue buffers. /// * `idx` - the index of the available ring entry where the driver would put the next /// available descriptor chain. /// * `queue` - the `Queue` object from which the needed data to create the `AvailIter` can /// be retrieved. pub(crate) fn new(mem: M, idx: Wrapping, queue: &'b mut Queue) -> Result { // The number of descriptor chain heads to process should always // be smaller or equal to the queue size, as the driver should // never ask the VMM to process a available ring entry more than // once. Checking and reporting such incorrect driver behavior // can prevent potential hanging and Denial-of-Service from // happening on the VMM side. if (idx - queue.next_avail).0 > queue.size { return Err(Error::InvalidAvailRingIndex); } Ok(AvailIter { mem, desc_table: queue.desc_table, avail_ring: queue.avail_ring, queue_size: queue.size, last_index: idx, next_avail: &mut queue.next_avail, }) } /// Goes back one position in the available descriptor chain offered by the driver. /// /// Rust does not support bidirectional iterators. This is the only way to revert the effect /// of an iterator increment on the queue. /// /// Note: this method assumes there's only one thread manipulating the queue, so it should only /// be invoked in single-threaded context. pub fn go_to_previous_position(&mut self) { *self.next_avail -= Wrapping(1); } } impl<'b, M> Iterator for AvailIter<'b, M> where M: Clone + Deref, M::Target: GuestMemory, { type Item = DescriptorChain; fn next(&mut self) -> Option { if *self.next_avail == self.last_index { return None; } // These two operations can not overflow an u64 since they're working with relatively small // numbers compared to u64::MAX. let elem_off = u64::from(self.next_avail.0.checked_rem(self.queue_size)?) * VIRTQ_AVAIL_ELEMENT_SIZE; let offset = VIRTQ_AVAIL_RING_HEADER_SIZE + elem_off; let addr = self.avail_ring.checked_add(offset)?; let head_index: u16 = self .mem .load(addr, Ordering::Acquire) .map(u16::from_le) .map_err(|_| error!("Failed to read from memory {:x}", addr.raw_value())) .ok()?; *self.next_avail += Wrapping(1); Some(DescriptorChain::new( self.mem.clone(), self.desc_table, self.queue_size, head_index, )) } } #[cfg(any(test, feature = "test-utils"))] // It is convenient for tests to implement `PartialEq`, but it is not a // proper implementation as `GuestMemory` errors cannot implement `PartialEq`. impl PartialEq for Error { fn eq(&self, other: &Self) -> bool { format!("{}", &self) == format!("{}", other) } } #[cfg(test)] mod tests { use super::*; use crate::defs::{DEFAULT_AVAIL_RING_ADDR, DEFAULT_DESC_TABLE_ADDR, DEFAULT_USED_RING_ADDR}; use crate::mock::MockSplitQueue; use crate::Descriptor; use virtio_bindings::bindings::virtio_ring::{ VRING_DESC_F_NEXT, VRING_DESC_F_WRITE, VRING_USED_F_NO_NOTIFY, }; use vm_memory::{Address, Bytes, GuestAddress, GuestMemoryMmap}; #[test] fn test_queue_is_valid() { let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(m, 16); let mut q: Queue = vq.create_queue().unwrap(); // q is currently valid assert!(q.is_valid(m)); // shouldn't be valid when not marked as ready q.set_ready(false); assert!(!q.ready()); assert!(!q.is_valid(m)); q.set_ready(true); // shouldn't be allowed to set a size > max_size q.set_size(q.max_size() << 1); assert_eq!(q.size, q.max_size()); // or set the size to 0 q.set_size(0); assert_eq!(q.size, q.max_size()); // or set a size which is not a power of 2 q.set_size(11); assert_eq!(q.size, q.max_size()); // but should be allowed to set a size if 0 < size <= max_size and size is a power of two q.set_size(4); assert_eq!(q.size, 4); q.size = q.max_size(); // shouldn't be allowed to set an address that breaks the alignment constraint q.set_desc_table_address(Some(0xf), None); assert_eq!(q.desc_table.0, vq.desc_table_addr().0); // should be allowed to set an aligned out of bounds address q.set_desc_table_address(Some(0xffff_fff0), None); assert_eq!(q.desc_table.0, 0xffff_fff0); // but shouldn't be valid assert!(!q.is_valid(m)); // but should be allowed to set a valid description table address q.set_desc_table_address(Some(0x10), None); assert_eq!(q.desc_table.0, 0x10); assert!(q.is_valid(m)); let addr = vq.desc_table_addr().0; q.set_desc_table_address(Some(addr as u32), Some((addr >> 32) as u32)); // shouldn't be allowed to set an address that breaks the alignment constraint q.set_avail_ring_address(Some(0x1), None); assert_eq!(q.avail_ring.0, vq.avail_addr().0); // should be allowed to set an aligned out of bounds address q.set_avail_ring_address(Some(0xffff_fffe), None); assert_eq!(q.avail_ring.0, 0xffff_fffe); // but shouldn't be valid assert!(!q.is_valid(m)); // but should be allowed to set a valid available ring address q.set_avail_ring_address(Some(0x2), None); assert_eq!(q.avail_ring.0, 0x2); assert!(q.is_valid(m)); let addr = vq.avail_addr().0; q.set_avail_ring_address(Some(addr as u32), Some((addr >> 32) as u32)); // shouldn't be allowed to set an address that breaks the alignment constraint q.set_used_ring_address(Some(0x3), None); assert_eq!(q.used_ring.0, vq.used_addr().0); // should be allowed to set an aligned out of bounds address q.set_used_ring_address(Some(0xffff_fffc), None); assert_eq!(q.used_ring.0, 0xffff_fffc); // but shouldn't be valid assert!(!q.is_valid(m)); // but should be allowed to set a valid used ring address q.set_used_ring_address(Some(0x4), None); assert_eq!(q.used_ring.0, 0x4); let addr = vq.used_addr().0; q.set_used_ring_address(Some(addr as u32), Some((addr >> 32) as u32)); assert!(q.is_valid(m)); } #[test] fn test_add_used() { let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(mem, 16); let mut q: Queue = vq.create_queue().unwrap(); assert_eq!(q.used_idx(mem, Ordering::Acquire).unwrap(), Wrapping(0)); assert_eq!(u16::from_le(vq.used().idx().load()), 0); // index too large assert!(q.add_used(mem, 16, 0x1000).is_err()); assert_eq!(u16::from_le(vq.used().idx().load()), 0); // should be ok q.add_used(mem, 1, 0x1000).unwrap(); assert_eq!(q.next_used, Wrapping(1)); assert_eq!(q.used_idx(mem, Ordering::Acquire).unwrap(), Wrapping(1)); assert_eq!(u16::from_le(vq.used().idx().load()), 1); let x = vq.used().ring().ref_at(0).unwrap().load(); assert_eq!(x.id(), 1); assert_eq!(x.len(), 0x1000); } #[test] fn test_reset_queue() { let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(m, 16); let mut q: Queue = vq.create_queue().unwrap(); q.set_size(8); // The address set by `MockSplitQueue` for the descriptor table is DEFAULT_DESC_TABLE_ADDR, // so let's change it for testing the reset. q.set_desc_table_address(Some(0x5000), None); // Same for `event_idx_enabled`, `next_avail` `next_used` and `signalled_used`. q.set_event_idx(true); q.set_next_avail(2); q.set_next_used(4); q.num_added = Wrapping(15); assert_eq!(q.size, 8); // `create_queue` also marks the queue as ready. assert!(q.ready); assert_ne!(q.desc_table, GuestAddress(DEFAULT_DESC_TABLE_ADDR)); assert_ne!(q.avail_ring, GuestAddress(DEFAULT_AVAIL_RING_ADDR)); assert_ne!(q.used_ring, GuestAddress(DEFAULT_USED_RING_ADDR)); assert_ne!(q.next_avail, Wrapping(0)); assert_ne!(q.next_used, Wrapping(0)); assert_ne!(q.num_added, Wrapping(0)); assert!(q.event_idx_enabled); q.reset(); assert_eq!(q.size, 16); assert!(!q.ready); assert_eq!(q.desc_table, GuestAddress(DEFAULT_DESC_TABLE_ADDR)); assert_eq!(q.avail_ring, GuestAddress(DEFAULT_AVAIL_RING_ADDR)); assert_eq!(q.used_ring, GuestAddress(DEFAULT_USED_RING_ADDR)); assert_eq!(q.next_avail, Wrapping(0)); assert_eq!(q.next_used, Wrapping(0)); assert_eq!(q.num_added, Wrapping(0)); assert!(!q.event_idx_enabled); } #[test] fn test_needs_notification() { let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let qsize = 16; let vq = MockSplitQueue::new(mem, qsize); let mut q: Queue = vq.create_queue().unwrap(); let avail_addr = vq.avail_addr(); // It should always return true when EVENT_IDX isn't enabled. for i in 0..qsize { q.next_used = Wrapping(i); assert!(q.needs_notification(mem).unwrap()); } mem.write_obj::( u16::to_le(4), avail_addr.unchecked_add(4 + qsize as u64 * 2), ) .unwrap(); q.set_event_idx(true); // Incrementing up to this value causes an `u16` to wrap back to 0. let wrap = u32::from(u16::MAX) + 1; for i in 0..wrap + 12 { q.next_used = Wrapping(i as u16); // Let's test wrapping around the maximum index value as well. // `num_added` needs to be at least `1` to represent the fact that new descriptor // chains have be added to the used ring since the last time `needs_notification` // returned. q.num_added = Wrapping(1); let expected = i == 5 || i == (5 + wrap); assert_eq!((q.needs_notification(mem).unwrap(), i), (expected, i)); } mem.write_obj::( u16::to_le(8), avail_addr.unchecked_add(4 + qsize as u64 * 2), ) .unwrap(); // Returns `false` because the current `used_event` value is behind both `next_used` and // the value of `next_used` at the time when `needs_notification` last returned (which is // computed based on `num_added` as described in the comments for `needs_notification`. assert!(!q.needs_notification(mem).unwrap()); mem.write_obj::( u16::to_le(15), avail_addr.unchecked_add(4 + qsize as u64 * 2), ) .unwrap(); q.num_added = Wrapping(1); assert!(!q.needs_notification(mem).unwrap()); q.next_used = Wrapping(15); q.num_added = Wrapping(1); assert!(!q.needs_notification(mem).unwrap()); q.next_used = Wrapping(16); q.num_added = Wrapping(1); assert!(q.needs_notification(mem).unwrap()); // Calling `needs_notification` again immediately returns `false`. assert!(!q.needs_notification(mem).unwrap()); mem.write_obj::( u16::to_le(u16::MAX - 3), avail_addr.unchecked_add(4 + qsize as u64 * 2), ) .unwrap(); q.next_used = Wrapping(u16::MAX - 2); q.num_added = Wrapping(1); // Returns `true` because, when looking at circular sequence of indices of the used ring, // the value we wrote in the `used_event` appears between the "old" value of `next_used` // (i.e. `next_used` - `num_added`) and the current `next_used`, thus suggesting that we // need to notify the driver. assert!(q.needs_notification(mem).unwrap()); } #[test] fn test_enable_disable_notification() { let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(mem, 16); let mut q: Queue = vq.create_queue().unwrap(); let used_addr = vq.used_addr(); assert!(!q.event_idx_enabled); q.enable_notification(mem).unwrap(); let v = mem.read_obj::(used_addr).map(u16::from_le).unwrap(); assert_eq!(v, 0); q.disable_notification(mem).unwrap(); let v = mem.read_obj::(used_addr).map(u16::from_le).unwrap(); assert_eq!(v, VRING_USED_F_NO_NOTIFY as u16); q.enable_notification(mem).unwrap(); let v = mem.read_obj::(used_addr).map(u16::from_le).unwrap(); assert_eq!(v, 0); q.set_event_idx(true); let avail_addr = vq.avail_addr(); mem.write_obj::(u16::to_le(2), avail_addr.unchecked_add(2)) .unwrap(); assert!(q.enable_notification(mem).unwrap()); q.next_avail = Wrapping(2); assert!(!q.enable_notification(mem).unwrap()); mem.write_obj::(u16::to_le(8), avail_addr.unchecked_add(2)) .unwrap(); assert!(q.enable_notification(mem).unwrap()); q.next_avail = Wrapping(8); assert!(!q.enable_notification(mem).unwrap()); } #[test] fn test_consume_chains_with_notif() { let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(mem, 16); let mut q: Queue = vq.create_queue().unwrap(); // q is currently valid. assert!(q.is_valid(mem)); // The chains are (0, 1), (2, 3, 4), (5, 6), (7, 8), (9, 10, 11, 12). let mut descs = Vec::new(); for i in 0..13 { let flags = match i { 1 | 4 | 6 | 8 | 12 => 0, _ => VRING_DESC_F_NEXT, }; descs.push(Descriptor::new( (0x1000 * (i + 1)) as u64, 0x1000, flags as u16, i + 1, )); } vq.add_desc_chains(&descs, 0).unwrap(); // Update the index of the chain that can be consumed to not be the last one. // This enables us to consume chains in multiple iterations as opposed to consuming // all the driver written chains at once. vq.avail().idx().store(u16::to_le(2)); // No descriptor chains are consumed at this point. assert_eq!(q.next_avail(), 0); let mut i = 0; loop { i += 1; q.disable_notification(mem).unwrap(); while let Some(chain) = q.iter(mem).unwrap().next() { // Process the descriptor chain, and then add entries to the // used ring. let head_index = chain.head_index(); let mut desc_len = 0; chain.for_each(|d| { if d.flags() as u32 & VRING_DESC_F_WRITE == VRING_DESC_F_WRITE { desc_len += d.len(); } }); q.add_used(mem, head_index, desc_len).unwrap(); } if !q.enable_notification(mem).unwrap() { break; } } // The chains should be consumed in a single loop iteration because there's nothing updating // the `idx` field of the available ring in the meantime. assert_eq!(i, 1); // The next chain that can be consumed should have index 2. assert_eq!(q.next_avail(), 2); assert_eq!(q.next_used(), 2); // Let the device know it can consume one more chain. vq.avail().idx().store(u16::to_le(3)); i = 0; loop { i += 1; q.disable_notification(mem).unwrap(); while let Some(chain) = q.iter(mem).unwrap().next() { // Process the descriptor chain, and then add entries to the // used ring. let head_index = chain.head_index(); let mut desc_len = 0; chain.for_each(|d| { if d.flags() as u32 & VRING_DESC_F_WRITE == VRING_DESC_F_WRITE { desc_len += d.len(); } }); q.add_used(mem, head_index, desc_len).unwrap(); } // For the simplicity of the test we are updating here the `idx` value of the available // ring. Ideally this should be done on a separate thread. // Because of this update, the loop should be iterated again to consume the new // available descriptor chains. vq.avail().idx().store(u16::to_le(4)); if !q.enable_notification(mem).unwrap() { break; } } assert_eq!(i, 2); // The next chain that can be consumed should have index 4. assert_eq!(q.next_avail(), 4); assert_eq!(q.next_used(), 4); // Set an `idx` that is bigger than the number of entries added in the ring. // This is an allowed scenario, but the indexes of the chain will have unexpected values. vq.avail().idx().store(u16::to_le(7)); loop { q.disable_notification(mem).unwrap(); while let Some(chain) = q.iter(mem).unwrap().next() { // Process the descriptor chain, and then add entries to the // used ring. let head_index = chain.head_index(); let mut desc_len = 0; chain.for_each(|d| { if d.flags() as u32 & VRING_DESC_F_WRITE == VRING_DESC_F_WRITE { desc_len += d.len(); } }); q.add_used(mem, head_index, desc_len).unwrap(); } if !q.enable_notification(mem).unwrap() { break; } } assert_eq!(q.next_avail(), 7); assert_eq!(q.next_used(), 7); } #[test] fn test_invalid_avail_idx() { // This is a negative test for the following MUST from the spec: `A driver MUST NOT // decrement the available idx on a virtqueue (ie. there is no way to “unexpose” buffers).`. // We validate that for this misconfiguration, the device does not panic. let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(mem, 16); let mut q: Queue = vq.create_queue().unwrap(); // q is currently valid. assert!(q.is_valid(mem)); // The chains are (0, 1), (2, 3, 4), (5, 6). let mut descs = Vec::new(); for i in 0..7 { let flags = match i { 1 | 4 | 6 => 0, _ => VRING_DESC_F_NEXT, }; descs.push(Descriptor::new( (0x1000 * (i + 1)) as u64, 0x1000, flags as u16, i + 1, )); } vq.add_desc_chains(&descs, 0).unwrap(); // Let the device know it can consume chains with the index < 2. vq.avail().idx().store(u16::to_le(3)); // No descriptor chains are consumed at this point. assert_eq!(q.next_avail(), 0); assert_eq!(q.next_used(), 0); loop { q.disable_notification(mem).unwrap(); while let Some(chain) = q.iter(mem).unwrap().next() { // Process the descriptor chain, and then add entries to the // used ring. let head_index = chain.head_index(); let mut desc_len = 0; chain.for_each(|d| { if d.flags() as u32 & VRING_DESC_F_WRITE == VRING_DESC_F_WRITE { desc_len += d.len(); } }); q.add_used(mem, head_index, desc_len).unwrap(); } if !q.enable_notification(mem).unwrap() { break; } } // The next chain that can be consumed should have index 3. assert_eq!(q.next_avail(), 3); assert_eq!(q.avail_idx(mem, Ordering::Acquire).unwrap(), Wrapping(3)); assert_eq!(q.next_used(), 3); assert_eq!(q.used_idx(mem, Ordering::Acquire).unwrap(), Wrapping(3)); assert!(q.lock().ready()); // Decrement `idx` which should be forbidden. We don't enforce this thing, but we should // test that we don't panic in case the driver decrements it. vq.avail().idx().store(u16::to_le(1)); // Invalid available ring index assert!(q.iter(mem).is_err()); } #[test] fn test_iterator_and_avail_idx() { // This test ensures constructing a descriptor chain iterator succeeds // with valid available ring indexes while produces an error with invalid // indexes. let queue_size = 2; let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(mem, queue_size); let mut q: Queue = vq.create_queue().unwrap(); // q is currently valid. assert!(q.is_valid(mem)); // Create descriptors to fill up the queue let mut descs = Vec::new(); for i in 0..queue_size { descs.push(Descriptor::new( (0x1000 * (i + 1)) as u64, 0x1000, 0_u16, i + 1, )); } vq.add_desc_chains(&descs, 0).unwrap(); // Set the 'next_available' index to 'u16:MAX' to test the wrapping scenarios q.set_next_avail(u16::MAX); // When the number of chains exposed by the driver is equal to or less than the queue // size, the available ring index is valid and constructs an iterator successfully. let avail_idx = Wrapping(q.next_avail()) + Wrapping(queue_size); vq.avail().idx().store(u16::to_le(avail_idx.0)); assert!(q.iter(mem).is_ok()); let avail_idx = Wrapping(q.next_avail()) + Wrapping(queue_size - 1); vq.avail().idx().store(u16::to_le(avail_idx.0)); assert!(q.iter(mem).is_ok()); // When the number of chains exposed by the driver is larger than the queue size, the // available ring index is invalid and produces an error from constructing an iterator. let avail_idx = Wrapping(q.next_avail()) + Wrapping(queue_size + 1); vq.avail().idx().store(u16::to_le(avail_idx.0)); assert!(q.iter(mem).is_err()); } #[test] fn test_descriptor_and_iterator() { let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(m, 16); let mut q: Queue = vq.create_queue().unwrap(); // q is currently valid assert!(q.is_valid(m)); // the chains are (0, 1), (2, 3, 4) and (5, 6) let mut descs = Vec::new(); for j in 0..7 { let flags = match j { 1 | 6 => 0, 2 | 5 => VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, 4 => VRING_DESC_F_WRITE, _ => VRING_DESC_F_NEXT, }; descs.push(Descriptor::new( (0x1000 * (j + 1)) as u64, 0x1000, flags as u16, j + 1, )); } vq.add_desc_chains(&descs, 0).unwrap(); let mut i = q.iter(m).unwrap(); { let c = i.next().unwrap(); assert_eq!(c.head_index(), 0); let mut iter = c; assert!(iter.next().is_some()); assert!(iter.next().is_some()); assert!(iter.next().is_none()); assert!(iter.next().is_none()); } { let c = i.next().unwrap(); assert_eq!(c.head_index(), 2); let mut iter = c.writable(); assert!(iter.next().is_some()); assert!(iter.next().is_some()); assert!(iter.next().is_none()); assert!(iter.next().is_none()); } { let c = i.next().unwrap(); assert_eq!(c.head_index(), 5); let mut iter = c.readable(); assert!(iter.next().is_some()); assert!(iter.next().is_none()); assert!(iter.next().is_none()); } } #[test] fn test_iterator() { let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(m, 16); let mut q: Queue = vq.create_queue().unwrap(); q.size = q.max_size; q.desc_table = vq.desc_table_addr(); q.avail_ring = vq.avail_addr(); q.used_ring = vq.used_addr(); assert!(q.is_valid(m)); { // an invalid queue should return an iterator with no next q.ready = false; assert!(q.iter(m).is_err()); } q.ready = true; // now let's create two simple descriptor chains // the chains are (0, 1) and (2, 3, 4) { let mut descs = Vec::new(); for j in 0..5u16 { let flags = match j { 1 | 4 => 0, _ => VRING_DESC_F_NEXT, }; descs.push(Descriptor::new( (0x1000 * (j + 1)) as u64, 0x1000, flags as u16, j + 1, )); } vq.add_desc_chains(&descs, 0).unwrap(); let mut i = q.iter(m).unwrap(); { let mut c = i.next().unwrap(); assert_eq!(c.head_index(), 0); c.next().unwrap(); assert!(c.next().is_some()); assert!(c.next().is_none()); assert_eq!(c.head_index(), 0); } { let mut c = i.next().unwrap(); assert_eq!(c.head_index(), 2); c.next().unwrap(); c.next().unwrap(); c.next().unwrap(); assert!(c.next().is_none()); assert_eq!(c.head_index(), 2); } // also test go_to_previous_position() works as expected { assert!(i.next().is_none()); i.go_to_previous_position(); let mut c = q.iter(m).unwrap().next().unwrap(); c.next().unwrap(); c.next().unwrap(); c.next().unwrap(); assert!(c.next().is_none()); } } // Test that iterating some broken descriptor chain does not exceed // 2^32 bytes in total (VIRTIO spec version 1.2, 2.7.5.2: // Drivers MUST NOT add a descriptor chain longer than 2^32 bytes in // total) { let descs = vec![ Descriptor::new(0x1000, 0xffff_ffff, VRING_DESC_F_NEXT as u16, 1), Descriptor::new(0x1000, 0x1234_5678, 0, 2), ]; vq.add_desc_chains(&descs, 0).unwrap(); let mut yielded_bytes_by_iteration = 0_u32; for d in q.iter(m).unwrap().next().unwrap() { yielded_bytes_by_iteration = yielded_bytes_by_iteration .checked_add(d.len()) .expect("iterator should not yield more than 2^32 bytes"); } } // Same as above, but test with a descriptor which is self-referential { let descs = vec![Descriptor::new( 0x1000, 0xffff_ffff, VRING_DESC_F_NEXT as u16, 0, )]; vq.add_desc_chains(&descs, 0).unwrap(); let mut yielded_bytes_by_iteration = 0_u32; for d in q.iter(m).unwrap().next().unwrap() { yielded_bytes_by_iteration = yielded_bytes_by_iteration .checked_add(d.len()) .expect("iterator should not yield more than 2^32 bytes"); } } } #[test] fn test_regression_iterator_division() { // This is a regression test that tests that the iterator does not try to divide // by 0 when the queue size is 0 let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(m, 1); // This input was generated by the fuzzer, both for the QueueS and the Descriptor let descriptors: Vec = vec![Descriptor::new( 14178673876262995140, 3301229764, 50372, 50372, )]; vq.build_desc_chain(&descriptors).unwrap(); let mut q = Queue { max_size: 38, next_avail: Wrapping(0), next_used: Wrapping(0), event_idx_enabled: false, num_added: Wrapping(0), size: 0, ready: false, desc_table: GuestAddress(12837708984796196), avail_ring: GuestAddress(0), used_ring: GuestAddress(9943947977301164032), }; assert!(q.pop_descriptor_chain(m).is_none()); } #[test] fn test_setters_error_cases() { assert_eq!(Queue::new(15).unwrap_err(), Error::InvalidMaxSize); let mut q = Queue::new(16).unwrap(); let expected_val = q.desc_table.0; assert_eq!( q.try_set_desc_table_address(GuestAddress(0xf)).unwrap_err(), Error::InvalidDescTableAlign ); assert_eq!(q.desc_table(), expected_val); let expected_val = q.avail_ring.0; assert_eq!( q.try_set_avail_ring_address(GuestAddress(0x1)).unwrap_err(), Error::InvalidAvailRingAlign ); assert_eq!(q.avail_ring(), expected_val); let expected_val = q.used_ring.0; assert_eq!( q.try_set_used_ring_address(GuestAddress(0x3)).unwrap_err(), Error::InvalidUsedRingAlign ); assert_eq!(q.used_ring(), expected_val); let expected_val = q.size; assert_eq!(q.try_set_size(15).unwrap_err(), Error::InvalidSize); assert_eq!(q.size(), expected_val) } #[test] // This is a regression test for a fuzzing finding. If the driver requests a reset of the // device, but then does not re-initializes the queue then a subsequent call to process // a request should yield no descriptors to process. Before this fix we were processing // descriptors that were added to the queue before, and we were ending up processing 255 // descriptors per chain. fn test_regression_timeout_after_reset() { // The input below was generated by libfuzzer and adapted for this test. let m = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0x0), 0x10000)]).unwrap(); let vq = MockSplitQueue::new(m, 1024); // This input below was generated by the fuzzer. let descriptors: Vec = vec![ Descriptor::new(21508325467, 0, 1, 4), Descriptor::new(2097152, 4096, 3, 0), Descriptor::new(18374686479672737792, 4294967295, 65535, 29), Descriptor::new(76842670169653248, 1114115, 0, 0), Descriptor::new(16, 983040, 126, 3), Descriptor::new(897648164864, 0, 0, 0), Descriptor::new(111669149722, 0, 0, 0), ]; vq.build_multiple_desc_chains(&descriptors).unwrap(); let mut q: Queue = vq.create_queue().unwrap(); // Setting the queue to ready should not allow consuming descriptors after reset. q.reset(); q.set_ready(true); let mut counter = 0; while let Some(mut desc_chain) = q.pop_descriptor_chain(m) { // this empty loop is here to check that there are no side effects // in terms of memory & execution time. while desc_chain.next().is_some() { counter += 1; } } assert_eq!(counter, 0); // Setting the avail_addr to valid should not allow consuming descriptors after reset. q.reset(); q.set_avail_ring_address(Some(0x1000), None); assert_eq!(q.avail_ring, GuestAddress(0x1000)); counter = 0; while let Some(mut desc_chain) = q.pop_descriptor_chain(m) { // this empty loop is here to check that there are no side effects // in terms of memory & execution time. while desc_chain.next().is_some() { counter += 1; } } assert_eq!(counter, 0); } }