@@ -48,7 +48,7 @@ impl fmt::Display for QueueError {
4848 }
4949}
5050
51- /// A virtio descriptor constraints with C representive .
51+ /// A virtio descriptor constraints with C representative .
5252#[ repr( C ) ]
5353#[ derive( Default , Clone , Copy ) ]
5454struct Descriptor {
@@ -103,9 +103,9 @@ impl<'a> DescriptorChain<'a> {
103103 // These reads can't fail unless Guest memory is hopelessly broken.
104104 let desc = match mem. read_obj :: < Descriptor > ( desc_head) {
105105 Ok ( ret) => ret,
106- Err ( _ ) => {
106+ Err ( err ) => {
107107 // TODO log address
108- error ! ( "Failed to read from memory" ) ;
108+ error ! ( "Failed to read virtio descriptor from memory: {}" , err ) ;
109109 return None ;
110110 }
111111 } ;
@@ -288,7 +288,7 @@ impl Queue {
288288 }
289289
290290 /// Returns the number of yet-to-be-popped descriptor chains in the avail ring.
291- pub fn len ( & self , mem : & GuestMemoryMmap ) -> u16 {
291+ fn len ( & self , mem : & GuestMemoryMmap ) -> u16 {
292292 ( self . avail_idx ( mem) - self . next_avail ) . 0
293293 }
294294
@@ -299,7 +299,21 @@ impl Queue {
299299
300300 /// Pop the first available descriptor chain from the avail ring.
301301 pub fn pop < ' a , ' b > ( & ' a mut self , mem : & ' b GuestMemoryMmap ) -> Option < DescriptorChain < ' b > > {
302- if self . len ( mem) == 0 {
302+ let len = self . len ( mem) ;
303+ // The number of descriptor chain heads to process should always
304+ // be smaller or equal to the queue size, as the driver should
305+ // never ask the VMM to process a available ring entry more than
306+ // once. Checking and reporting such incorrect driver behavior
307+ // can prevent potential hanging and Denial-of-Service from
308+ // happening on the VMM side.
309+ if len > self . actual_size ( ) {
310+ // We are choosing to interrupt execution since this could be a potential malicious
311+ // driver scenario. This way we also eliminate the risk of repeatedly
312+ // logging and potentially clogging the microVM through the log system.
313+ panic ! ( "The number of available virtio descriptors is greater than queue size!" ) ;
314+ }
315+
316+ if len == 0 {
303317 return None ;
304318 }
305319
@@ -424,9 +438,9 @@ impl Queue {
424438 fn avail_idx ( & self , mem : & GuestMemoryMmap ) -> Wrapping < u16 > {
425439 // Bound checks for queue inner data have already been performed, at device activation time,
426440 // via `self.is_valid()`, so it's safe to unwrap and use unchecked offsets here.
427- // Note: the `MmioTransport` code ensures that queue addresses cannot be changed by the guest
428- // after device activation, so we can be certain that no change has occured since
429- // the last `self.is_valid()` check.
441+ // Note: the `MmioTransport` code ensures that queue addresses cannot be changed by the
442+ // guest after device activation, so we can be certain that no change has
443+ // occurred since the last `self.is_valid()` check.
430444 let addr = self . avail_ring . unchecked_add ( 2 ) ;
431445 Wrapping ( mem. read_obj :: < u16 > ( addr) . unwrap ( ) )
432446 }
@@ -462,7 +476,17 @@ impl Queue {
462476 return true ;
463477 }
464478
465- if self . len ( mem) != 0 {
479+ let len = self . len ( mem) ;
480+ if len != 0 {
481+ // The number of descriptor chain heads to process should always
482+ // be smaller or equal to the queue size.
483+ if len > self . actual_size ( ) {
484+ // We are choosing to interrupt execution since this could be a potential malicious
485+ // driver scenario. This way we also eliminate the risk of
486+ // repeatedly logging and potentially clogging the microVM through
487+ // the log system.
488+ panic ! ( "The number of available virtio descriptors is greater than queue size!" ) ;
489+ }
466490 return false ;
467491 }
468492
@@ -741,6 +765,92 @@ pub(crate) mod tests {
741765 assert_eq ! ( q. avail_event( m) , 2 ) ;
742766 }
743767
768+ #[ test]
769+ #[ should_panic(
770+ expected = "The number of available virtio descriptors is greater than queue size!"
771+ ) ]
772+ fn test_invalid_avail_idx_no_notification ( ) {
773+ // This test ensures constructing a descriptor chain succeeds
774+ // with valid available ring indexes while it produces an error with invalid
775+ // indexes.
776+ // No notification suppression enabled.
777+ let m = & create_anon_guest_memory ( & [ ( GuestAddress ( 0 ) , 0x6000 ) ] , false ) . unwrap ( ) ;
778+
779+ // We set up a queue of size 4.
780+ let vq = VirtQueue :: new ( GuestAddress ( 0 ) , m, 4 ) ;
781+ let mut q = vq. create_queue ( ) ;
782+
783+ for j in 0 ..4 {
784+ vq. dtable [ j] . set (
785+ 0x1000 * ( j + 1 ) as u64 ,
786+ 0x1000 ,
787+ VIRTQ_DESC_F_NEXT ,
788+ ( j + 1 ) as u16 ,
789+ ) ;
790+ }
791+
792+ // Create 2 descriptor chains.
793+ // the chains are (0, 1) and (2, 3)
794+ vq. dtable [ 1 ] . flags . set ( 0 ) ;
795+ vq. dtable [ 3 ] . flags . set ( 0 ) ;
796+ vq. avail . ring [ 0 ] . set ( 0 ) ;
797+ vq. avail . ring [ 1 ] . set ( 2 ) ;
798+ vq. avail . idx . set ( 2 ) ;
799+
800+ // We've just set up two chains.
801+ assert_eq ! ( q. len( m) , 2 ) ;
802+
803+ // We process the first descriptor.
804+ let d = q. pop ( m) . unwrap ( ) . next_descriptor ( ) ;
805+ assert ! ( matches!( d, Some ( x) if !x. has_next( ) ) ) ;
806+ // We confuse the device and set the available index as being 6.
807+ vq. avail . idx . set ( 6 ) ;
808+
809+ // We've actually just popped a descriptor so 6 - 1 = 5.
810+ assert_eq ! ( q. len( m) , 5 ) ;
811+
812+ // However, since the apparent length set by the driver is more than the queue size,
813+ // we would be running the risk of going through some descriptors more than once.
814+ // As such, we expect to panic.
815+ q. pop ( m) ;
816+ }
817+
818+ #[ test]
819+ #[ should_panic(
820+ expected = "The number of available virtio descriptors is greater than queue size!"
821+ ) ]
822+ fn test_invalid_avail_idx_with_notification ( ) {
823+ // This test ensures constructing a descriptor chain succeeds
824+ // with valid available ring indexes while it produces an error with invalid
825+ // indexes.
826+ // Notification suppression is enabled.
827+ let m = & create_anon_guest_memory ( & [ ( GuestAddress ( 0 ) , 0x6000 ) ] , false ) . unwrap ( ) ;
828+
829+ // We set up a queue of size 4.
830+ let vq = VirtQueue :: new ( GuestAddress ( 0 ) , m, 4 ) ;
831+ let mut q = vq. create_queue ( ) ;
832+
833+ q. uses_notif_suppression = true ;
834+
835+ // Create 1 descriptor chain of 4.
836+ for j in 0 ..4 {
837+ vq. dtable [ j] . set (
838+ 0x1000 * ( j + 1 ) as u64 ,
839+ 0x1000 ,
840+ VIRTQ_DESC_F_NEXT ,
841+ ( j + 1 ) as u16 ,
842+ ) ;
843+ }
844+ // We need to clear the VIRTQ_DESC_F_NEXT for the last descriptor.
845+ vq. dtable [ 3 ] . flags . set ( 0 ) ;
846+ vq. avail . ring [ 0 ] . set ( 0 ) ;
847+
848+ // driver sets available index to suspicious value.
849+ vq. avail . idx . set ( 6 ) ;
850+
851+ q. pop_or_enable_notification ( m) ;
852+ }
853+
744854 #[ test]
745855 fn test_add_used ( ) {
746856 let m = & create_anon_guest_memory ( & [ ( GuestAddress ( 0 ) , 0x10000 ) ] , false ) . unwrap ( ) ;
0 commit comments