@@ -49,7 +49,7 @@ impl fmt::Display for QueueError {
4949 }
5050}
5151
52- /// A virtio descriptor constraints with C representive .
52+ /// A virtio descriptor constraints with C representative .
5353#[ repr( C ) ]
5454#[ derive( Default , Clone , Copy ) ]
5555struct Descriptor {
@@ -104,9 +104,9 @@ impl<'a> DescriptorChain<'a> {
104104 // These reads can't fail unless Guest memory is hopelessly broken.
105105 let desc = match mem. read_obj :: < Descriptor > ( desc_head) {
106106 Ok ( ret) => ret,
107- Err ( _ ) => {
107+ Err ( err ) => {
108108 // TODO log address
109- error ! ( "Failed to read from memory" ) ;
109+ error ! ( "Failed to read virtio descriptor from memory: {}" , err ) ;
110110 return None ;
111111 }
112112 } ;
@@ -289,7 +289,7 @@ impl Queue {
289289 }
290290
291291 /// Returns the number of yet-to-be-popped descriptor chains in the avail ring.
292- pub fn len ( & self , mem : & GuestMemoryMmap ) -> u16 {
292+ fn len ( & self , mem : & GuestMemoryMmap ) -> u16 {
293293 ( self . avail_idx ( mem) - self . next_avail ) . 0
294294 }
295295
@@ -300,7 +300,21 @@ impl Queue {
300300
301301 /// Pop the first available descriptor chain from the avail ring.
302302 pub fn pop < ' a , ' b > ( & ' a mut self , mem : & ' b GuestMemoryMmap ) -> Option < DescriptorChain < ' b > > {
303- if self . len ( mem) == 0 {
303+ let len = self . len ( mem) ;
304+ // The number of descriptor chain heads to process should always
305+ // be smaller or equal to the queue size, as the driver should
306+ // never ask the VMM to process a available ring entry more than
307+ // once. Checking and reporting such incorrect driver behavior
308+ // can prevent potential hanging and Denial-of-Service from
309+ // happening on the VMM side.
310+ if len > self . actual_size ( ) {
311+ // We are choosing to interrupt execution since this could be a potential malicious
312+ // driver scenario. This way we also eliminate the risk of repeatedly
313+ // logging and potentially clogging the microVM through the log system.
314+ panic ! ( "The number of available virtio descriptors is greater than queue size!" ) ;
315+ }
316+
317+ if len == 0 {
304318 return None ;
305319 }
306320
@@ -425,7 +439,7 @@ impl Queue {
425439 // via `self.is_valid()`, so it's safe to unwrap and use unchecked offsets here.
426440 // Note: the `MmioTransport` code ensures that queue addresses cannot be changed by the
427441 // guest after device activation, so we can be certain that no change has
428- // occured since the last `self.is_valid()` check.
442+ // occurred since the last `self.is_valid()` check.
429443 let addr = self . avail_ring . unchecked_add ( 2 ) ;
430444 Wrapping ( mem. read_obj :: < u16 > ( addr) . unwrap ( ) )
431445 }
@@ -461,7 +475,17 @@ impl Queue {
461475 return true ;
462476 }
463477
464- if self . len ( mem) != 0 {
478+ let len = self . len ( mem) ;
479+ if len != 0 {
480+ // The number of descriptor chain heads to process should always
481+ // be smaller or equal to the queue size.
482+ if len > self . actual_size ( ) {
483+ // We are choosing to interrupt execution since this could be a potential malicious
484+ // driver scenario. This way we also eliminate the risk of
485+ // repeatedly logging and potentially clogging the microVM through
486+ // the log system.
487+ panic ! ( "The number of available virtio descriptors is greater than queue size!" ) ;
488+ }
465489 return false ;
466490 }
467491
@@ -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