@@ -48,7 +48,7 @@ impl fmt::Display for QueueError {
48
48
}
49
49
}
50
50
51
- /// A virtio descriptor constraints with C representive .
51
+ /// A virtio descriptor constraints with C representative .
52
52
#[ repr( C ) ]
53
53
#[ derive( Default , Clone , Copy ) ]
54
54
struct Descriptor {
@@ -103,9 +103,9 @@ impl<'a> DescriptorChain<'a> {
103
103
// These reads can't fail unless Guest memory is hopelessly broken.
104
104
let desc = match mem. read_obj :: < Descriptor > ( desc_head) {
105
105
Ok ( ret) => ret,
106
- Err ( _ ) => {
106
+ Err ( err ) => {
107
107
// TODO log address
108
- error ! ( "Failed to read from memory" ) ;
108
+ error ! ( "Failed to read virtio descriptor from memory: {}" , err ) ;
109
109
return None ;
110
110
}
111
111
} ;
@@ -288,7 +288,7 @@ impl Queue {
288
288
}
289
289
290
290
/// 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 {
292
292
( self . avail_idx ( mem) - self . next_avail ) . 0
293
293
}
294
294
@@ -299,7 +299,21 @@ impl Queue {
299
299
300
300
/// Pop the first available descriptor chain from the avail ring.
301
301
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 {
303
317
return None ;
304
318
}
305
319
@@ -424,9 +438,9 @@ impl Queue {
424
438
fn avail_idx ( & self , mem : & GuestMemoryMmap ) -> Wrapping < u16 > {
425
439
// Bound checks for queue inner data have already been performed, at device activation time,
426
440
// 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.
430
444
let addr = self . avail_ring . unchecked_add ( 2 ) ;
431
445
Wrapping ( mem. read_obj :: < u16 > ( addr) . unwrap ( ) )
432
446
}
@@ -462,7 +476,17 @@ impl Queue {
462
476
return true ;
463
477
}
464
478
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
+ }
466
490
return false ;
467
491
}
468
492
@@ -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