@@ -272,6 +272,28 @@ impl Queue {
272
272
}
273
273
}
274
274
275
+ fn write_elem < M : GuestMemory > (
276
+ mem : & M ,
277
+ addr : GuestAddress ,
278
+ head_index : u16 ,
279
+ len : u32 ,
280
+ ) -> Result < ( ) , Error > {
281
+ mem. write_obj ( VirtqUsedElem :: new ( head_index. into ( ) , len) , addr)
282
+ . map_err ( Error :: GuestMemory )
283
+ }
284
+
285
+ fn store_elem < M : GuestMemory > (
286
+ mem : & M ,
287
+ used_ring : GuestAddress ,
288
+ next_used : u16 ,
289
+ ) -> Result < ( ) , Error > {
290
+ let addr = used_ring
291
+ . checked_add ( 2 )
292
+ . ok_or ( Error :: AddressOverflow ) ?;
293
+ mem. store ( u16:: to_le ( next_used) , addr, Ordering :: Release )
294
+ . map_err ( Error :: GuestMemory )
295
+ }
296
+
275
297
#[ cfg( kani) ]
276
298
#[ allow( dead_code) ]
277
299
mod verification {
@@ -281,7 +303,7 @@ mod verification {
281
303
use std:: num:: Wrapping ;
282
304
use vm_memory:: FileOffset ;
283
305
284
- use vm_memory:: { GuestMemoryRegion , MemoryRegionAddress } ;
306
+ use vm_memory:: { GuestRegionMmap , GuestMemoryRegion , MemoryRegionAddress , AtomicAccess , GuestMemory , GuestMemoryError } ;
285
307
286
308
use super :: * ;
287
309
@@ -517,8 +539,47 @@ mod verification {
517
539
// So we do not care
518
540
}
519
541
}
520
- }
521
542
543
+ fn stub_write_elem < M : GuestMemory > (
544
+ _mem : & M ,
545
+ _addr : GuestAddress ,
546
+ _head_index : u16 ,
547
+ _len : u32 , ) -> Result < ( ) , Error > {
548
+ Ok ( ( ) )
549
+ }
550
+
551
+ fn stub_store_elem < M : GuestMemory > (
552
+ _mem : & M ,
553
+ _used_ring : GuestAddress ,
554
+ _next_used : u16 , ) -> Result < ( ) , Error > {
555
+ Ok ( ( ) )
556
+ }
557
+
558
+ #[ kani:: proof]
559
+ #[ kani:: unwind( 0 ) ]
560
+ #[ kani:: stub( write_elem, stub_write_elem) ]
561
+ #[ kani:: stub( store_elem, stub_store_elem) ]
562
+ fn verify_add_used ( ) {
563
+ let ProofContext ( mut queue, mem) = kani:: any ( ) ;
564
+ let used_idx = queue. next_used ;
565
+
566
+ let used_desc_table_index = kani:: any ( ) ;
567
+ if queue. add_used ( & mem, used_desc_table_index, kani:: any ( ) ) . is_ok ( ) {
568
+ assert_eq ! ( queue. next_used, used_idx + Wrapping ( 1 ) ) ;
569
+ } else {
570
+ assert_eq ! ( queue. next_used, used_idx) ;
571
+
572
+ // Ideally, here we would want to actually read the relevant values from memory and
573
+ // assert they are unchanged. However, kani will run out of memory if we try to do so,
574
+ // so we instead verify the following "proxy property": If an error happened, then
575
+ // it happened at the very beginning of add_used, meaning no memory accesses were
576
+ // done. This is relying on implementation details of add_used, namely that
577
+ // the check for out-of-bounds descriptor index happens at the very beginning of the
578
+ // function.
579
+ assert ! ( used_desc_table_index >= queue. size( ) ) ;
580
+ }
581
+ }
582
+ }
522
583
impl < ' a > QueueGuard < ' a > for Queue {
523
584
type G = & ' a mut Self ;
524
585
}
@@ -716,20 +777,12 @@ impl QueueT for Queue {
716
777
. used_ring
717
778
. checked_add ( offset)
718
779
. ok_or ( Error :: AddressOverflow ) ?;
719
- mem. write_obj ( VirtqUsedElem :: new ( head_index. into ( ) , len) , addr)
720
- . map_err ( Error :: GuestMemory ) ?;
780
+ write_elem ( mem, addr, head_index, len) ?;
721
781
722
782
self . next_used += Wrapping ( 1 ) ;
723
783
self . num_added += Wrapping ( 1 ) ;
724
784
725
- mem. store (
726
- u16:: to_le ( self . next_used . 0 ) ,
727
- self . used_ring
728
- . checked_add ( 2 )
729
- . ok_or ( Error :: AddressOverflow ) ?,
730
- Ordering :: Release ,
731
- )
732
- . map_err ( Error :: GuestMemory )
785
+ store_elem ( mem, self . used_ring , self . next_used . 0 )
733
786
}
734
787
735
788
fn enable_notification < M : GuestMemory > ( & mut self , mem : & M ) -> Result < bool , Error > {
0 commit comments