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