@@ -11,7 +11,7 @@ use std::sync::atomic::{Ordering, fence};
1111use crate :: arch:: host_page_size;
1212use crate :: logger:: error;
1313use crate :: utils:: u64_to_usize;
14- use crate :: vstate:: memory:: { Address , Bitmap , ByteValued , GuestAddress , GuestMemory } ;
14+ use crate :: vstate:: memory:: { Bitmap , ByteValued , GuestAddress , GuestMemory } ;
1515
1616pub const VIRTQ_DESC_F_NEXT : u16 = 0x1 ;
1717pub const VIRTQ_DESC_F_WRITE : u16 = 0x2 ;
@@ -35,6 +35,10 @@ pub enum QueueError {
3535 MemoryError ( #[ from] vm_memory:: GuestMemoryError ) ,
3636 /// Pointer is not aligned properly: {0:#x} not {1}-byte aligned.
3737 PointerNotAligned ( usize , usize ) ,
38+ /// Attempt to use virtio queue that is not marked ready
39+ NotReady ,
40+ /// Virtio queue with invalid size: {0}
41+ InvalidSize ( u16 ) ,
3842}
3943
4044/// Error type indicating the guest configured a virtio queue such that the avail_idx field would
@@ -339,6 +343,14 @@ impl Queue {
339343 /// Set up pointers to the queue objects in the guest memory
340344 /// and mark memory dirty for those objects
341345 pub fn initialize < M : GuestMemory > ( & mut self , mem : & M ) -> Result < ( ) , QueueError > {
346+ if !self . ready {
347+ return Err ( QueueError :: NotReady ) ;
348+ }
349+
350+ if self . size > self . max_size || self . size == 0 || ( self . size & ( self . size - 1 ) ) != 0 {
351+ return Err ( QueueError :: InvalidSize ( self . size ) ) ;
352+ }
353+
342354 // All the below pointers are verified to be aligned properly; otherwise some methods (e.g.
343355 // `read_volatile()`) will panic. Such an unalignment is possible when restored from a
344356 // broken/fuzzed snapshot.
@@ -446,58 +458,6 @@ impl Queue {
446458 }
447459 }
448460
449- /// Validates the queue's in-memory layout is correct.
450- pub fn is_valid < M : GuestMemory > ( & self , mem : & M ) -> bool {
451- let desc_table = self . desc_table_address ;
452- let desc_table_size = self . desc_table_size ( ) ;
453- let avail_ring = self . avail_ring_address ;
454- let avail_ring_size = self . avail_ring_size ( ) ;
455- let used_ring = self . used_ring_address ;
456- let used_ring_size = self . used_ring_size ( ) ;
457-
458- if !self . ready {
459- error ! ( "attempt to use virtio queue that is not marked ready" ) ;
460- false
461- } else if self . size > self . max_size || self . size == 0 || ( self . size & ( self . size - 1 ) ) != 0
462- {
463- error ! ( "virtio queue with invalid size: {}" , self . size) ;
464- false
465- } else if desc_table. raw_value ( ) & 0xf != 0 {
466- error ! ( "virtio queue descriptor table breaks alignment constraints" ) ;
467- false
468- } else if avail_ring. raw_value ( ) & 0x1 != 0 {
469- error ! ( "virtio queue available ring breaks alignment constraints" ) ;
470- false
471- } else if used_ring. raw_value ( ) & 0x3 != 0 {
472- error ! ( "virtio queue used ring breaks alignment constraints" ) ;
473- false
474- // range check entire descriptor table to be assigned valid guest physical addresses
475- } else if mem. get_slice ( desc_table, desc_table_size) . is_err ( ) {
476- error ! (
477- "virtio queue descriptor table goes out of bounds: start:0x{:08x} size:0x{:08x}" ,
478- desc_table. raw_value( ) ,
479- desc_table_size
480- ) ;
481- false
482- } else if mem. get_slice ( avail_ring, avail_ring_size) . is_err ( ) {
483- error ! (
484- "virtio queue available ring goes out of bounds: start:0x{:08x} size:0x{:08x}" ,
485- avail_ring. raw_value( ) ,
486- avail_ring_size
487- ) ;
488- false
489- } else if mem. get_slice ( used_ring, used_ring_size) . is_err ( ) {
490- error ! (
491- "virtio queue used ring goes out of bounds: start:0x{:08x} size:0x{:08x}" ,
492- used_ring. raw_value( ) ,
493- used_ring_size
494- ) ;
495- false
496- } else {
497- true
498- }
499- }
500-
501461 /// Returns the number of yet-to-be-popped descriptor chains in the avail ring.
502462 pub fn len ( & self ) -> u16 {
503463 ( Wrapping ( self . avail_ring_idx_get ( ) ) - self . next_avail ) . 0
@@ -901,8 +861,6 @@ mod verification {
901861 let mut queue = less_arbitrary_queue ( ) ;
902862 queue. initialize ( & mem) . unwrap ( ) ;
903863
904- assert ! ( queue. is_valid( & mem) ) ;
905-
906864 ProofContext ( queue, mem)
907865 }
908866 }
@@ -912,8 +870,7 @@ mod verification {
912870 let mem = setup_kani_guest_memory ( ) ;
913871 let mut queue: Queue = kani:: any ( ) ;
914872
915- kani:: assume ( queue. is_valid ( & mem) ) ;
916- queue. initialize ( & mem) . unwrap ( ) ;
873+ kani:: assume ( queue. initialize ( & mem) . is_ok ( ) ) ;
917874
918875 ProofContext ( queue, mem)
919876 }
@@ -1080,10 +1037,10 @@ mod verification {
10801037 #[ kani:: proof]
10811038 #[ kani:: unwind( 0 ) ]
10821039 #[ kani:: solver( cadical) ]
1083- fn verify_is_valid ( ) {
1040+ fn verify_initialize ( ) {
10841041 let ProofContext ( queue, mem) = kani:: any ( ) ;
10851042
1086- if queue. is_valid ( & mem) {
1043+ if queue. initialize ( & mem) . is_ok ( ) {
10871044 // Section 2.6: Alignment of descriptor table, available ring and used ring; size of
10881045 // queue
10891046 fn alignment_of ( val : u64 ) -> u64 {
@@ -1167,7 +1124,7 @@ mod verification {
11671124
11681125 // This is an assertion in pop which we use to abort firecracker in a ddos scenario
11691126 // This condition being false means that the guest is asking us to process every element
1170- // in the queue multiple times. It cannot be checked by is_valid , as that function
1127+ // in the queue multiple times. It cannot be checked by initialize , as that function
11711128 // is called when the queue is being initialized, e.g. empty. We compute it using
11721129 // local variables here to make things easier on kani: One less roundtrip through vm-memory.
11731130 let queue_len = queue. len ( ) ;
@@ -1252,7 +1209,7 @@ mod verification {
12521209
12531210#[ cfg( test) ]
12541211mod tests {
1255- use vm_memory:: Bytes ;
1212+ use vm_memory:: { Address , Bytes } ;
12561213
12571214 pub use super :: * ;
12581215 use crate :: devices:: virtio:: queue:: QueueError :: DescIndexOutOfBounds ;
@@ -1312,26 +1269,35 @@ mod tests {
13121269 let mut q = vq. create_queue ( ) ;
13131270
13141271 // q is currently valid
1315- assert ! ( q . is_valid ( m) ) ;
1272+ q . initialize ( m) . unwrap ( ) ;
13161273
13171274 // shouldn't be valid when not marked as ready
13181275 q. ready = false ;
1319- assert ! ( !q . is_valid ( m ) ) ;
1276+ assert ! ( matches! ( q . initialize ( m ) . unwrap_err ( ) , QueueError :: NotReady ) ) ;
13201277 q. ready = true ;
13211278
13221279 // or when size > max_size
13231280 q. size = q. max_size << 1 ;
1324- assert ! ( !q. is_valid( m) ) ;
1281+ assert ! ( matches!(
1282+ q. initialize( m) . unwrap_err( ) ,
1283+ QueueError :: InvalidSize ( _)
1284+ ) ) ;
13251285 q. size = q. max_size ;
13261286
13271287 // or when size is 0
13281288 q. size = 0 ;
1329- assert ! ( !q. is_valid( m) ) ;
1289+ assert ! ( matches!(
1290+ q. initialize( m) . unwrap_err( ) ,
1291+ QueueError :: InvalidSize ( _)
1292+ ) ) ;
13301293 q. size = q. max_size ;
13311294
13321295 // or when size is not a power of 2
13331296 q. size = 11 ;
1334- assert ! ( !q. is_valid( m) ) ;
1297+ assert ! ( matches!(
1298+ q. initialize( m) . unwrap_err( ) ,
1299+ QueueError :: InvalidSize ( _)
1300+ ) ) ;
13351301 q. size = q. max_size ;
13361302
13371303 // reset dirtied values
@@ -1342,22 +1308,40 @@ mod tests {
13421308
13431309 // or if the various addresses are off
13441310
1345- q. desc_table_address = GuestAddress ( 0xffff_ffff ) ;
1346- assert ! ( !q. is_valid( m) ) ;
1311+ q. desc_table_address = GuestAddress ( 0xffff_ff00 ) ;
1312+ assert ! ( matches!(
1313+ q. initialize( m) . unwrap_err( ) ,
1314+ QueueError :: MemoryError ( _)
1315+ ) ) ;
13471316 q. desc_table_address = GuestAddress ( 0x1001 ) ;
1348- assert ! ( !q. is_valid( m) ) ;
1317+ assert ! ( matches!(
1318+ q. initialize( m) . unwrap_err( ) ,
1319+ QueueError :: PointerNotAligned ( _, _)
1320+ ) ) ;
13491321 q. desc_table_address = vq. dtable_start ( ) ;
13501322
1351- q. avail_ring_address = GuestAddress ( 0xffff_ffff ) ;
1352- assert ! ( !q. is_valid( m) ) ;
1323+ q. avail_ring_address = GuestAddress ( 0xffff_ff00 ) ;
1324+ assert ! ( matches!(
1325+ q. initialize( m) . unwrap_err( ) ,
1326+ QueueError :: MemoryError ( _)
1327+ ) ) ;
13531328 q. avail_ring_address = GuestAddress ( 0x1001 ) ;
1354- assert ! ( !q. is_valid( m) ) ;
1329+ assert ! ( matches!(
1330+ q. initialize( m) . unwrap_err( ) ,
1331+ QueueError :: PointerNotAligned ( _, _)
1332+ ) ) ;
13551333 q. avail_ring_address = vq. avail_start ( ) ;
13561334
1357- q. used_ring_address = GuestAddress ( 0xffff_ffff ) ;
1358- assert ! ( !q. is_valid( m) ) ;
1335+ q. used_ring_address = GuestAddress ( 0xffff_ff00 ) ;
1336+ assert ! ( matches!(
1337+ q. initialize( m) . unwrap_err( ) ,
1338+ QueueError :: MemoryError ( _)
1339+ ) ) ;
13591340 q. used_ring_address = GuestAddress ( 0x1001 ) ;
1360- assert ! ( !q. is_valid( m) ) ;
1341+ assert ! ( matches!(
1342+ q. initialize( m) . unwrap_err( ) ,
1343+ QueueError :: PointerNotAligned ( _, _)
1344+ ) ) ;
13611345 q. used_ring_address = vq. used_start ( ) ;
13621346 }
13631347
@@ -1673,23 +1657,27 @@ mod tests {
16731657
16741658 #[ test]
16751659 fn test_initialize_with_aligned_pointer ( ) {
1676- let mut q = Queue :: new ( 0 ) ;
1660+ let mut q = Queue :: new ( FIRECRACKER_MAX_QUEUE_SIZE ) ;
1661+
1662+ q. ready = true ;
1663+ q. size = q. max_size ;
16771664
1678- let random_addr = 0x321 ;
16791665 // Descriptor table must be 16-byte aligned.
1680- q. desc_table_address = GuestAddress ( random_addr / 16 * 16 ) ;
1666+ q. desc_table_address = GuestAddress ( 16 ) ;
16811667 // Available ring must be 2-byte aligned.
1682- q. avail_ring_address = GuestAddress ( random_addr / 2 * 2 ) ;
1668+ q. avail_ring_address = GuestAddress ( 2 ) ;
16831669 // Used ring must be 4-byte aligned.
1684- q. avail_ring_address = GuestAddress ( random_addr / 4 * 4 ) ;
1670+ q. avail_ring_address = GuestAddress ( 4 ) ;
16851671
1686- let mem = single_region_mem ( 0x1000 ) ;
1672+ let mem = single_region_mem ( 0x10000 ) ;
16871673 q. initialize ( & mem) . unwrap ( ) ;
16881674 }
16891675
16901676 #[ test]
16911677 fn test_initialize_with_misaligned_pointer ( ) {
1692- let mut q = Queue :: new ( 0 ) ;
1678+ let mut q = Queue :: new ( FIRECRACKER_MAX_QUEUE_SIZE ) ;
1679+ q. ready = true ;
1680+ q. size = q. max_size ;
16931681 let mem = single_region_mem ( 0x1000 ) ;
16941682
16951683 // Descriptor table must be 16-byte aligned.
0 commit comments