@@ -49,7 +49,7 @@ impl fmt::Display for QueueError {
49
49
}
50
50
}
51
51
52
- /// A virtio descriptor constraints with C representive .
52
+ /// A virtio descriptor constraints with C representative .
53
53
#[ repr( C ) ]
54
54
#[ derive( Default , Clone , Copy ) ]
55
55
struct Descriptor {
@@ -104,9 +104,9 @@ impl<'a> DescriptorChain<'a> {
104
104
// These reads can't fail unless Guest memory is hopelessly broken.
105
105
let desc = match mem. read_obj :: < Descriptor > ( desc_head) {
106
106
Ok ( ret) => ret,
107
- Err ( _ ) => {
107
+ Err ( err ) => {
108
108
// TODO log address
109
- error ! ( "Failed to read from memory" ) ;
109
+ error ! ( "Failed to read virtio descriptor from memory: {}" , err ) ;
110
110
return None ;
111
111
}
112
112
} ;
@@ -289,7 +289,7 @@ impl Queue {
289
289
}
290
290
291
291
/// 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 {
293
293
( self . avail_idx ( mem) - self . next_avail ) . 0
294
294
}
295
295
@@ -300,7 +300,21 @@ impl Queue {
300
300
301
301
/// Pop the first available descriptor chain from the avail ring.
302
302
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 {
304
318
return None ;
305
319
}
306
320
@@ -425,7 +439,7 @@ impl Queue {
425
439
// via `self.is_valid()`, so it's safe to unwrap and use unchecked offsets here.
426
440
// Note: the `MmioTransport` code ensures that queue addresses cannot be changed by the
427
441
// 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.
429
443
let addr = self . avail_ring . unchecked_add ( 2 ) ;
430
444
Wrapping ( mem. read_obj :: < u16 > ( addr) . unwrap ( ) )
431
445
}
@@ -461,7 +475,17 @@ impl Queue {
461
475
return true ;
462
476
}
463
477
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
+ }
465
489
return false ;
466
490
}
467
491
@@ -741,6 +765,92 @@ pub(crate) mod tests {
741
765
assert_eq ! ( q. avail_event( m) , 2 ) ;
742
766
}
743
767
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
+
744
854
#[ test]
745
855
fn test_add_used ( ) {
746
856
let m = & create_anon_guest_memory ( & [ ( GuestAddress ( 0 ) , 0x10000 ) ] , false ) . unwrap ( ) ;
0 commit comments