@@ -509,3 +509,59 @@ fn verify_spec_2_7_7_2() {
509
509
// So we do not care
510
510
}
511
511
}
512
+
513
+ /// Reads the idx field from the used ring in guest memory, as stored by Queue::add_used.
514
+ /// Returns the value as u16.
515
+ fn get_used_idx (
516
+ queue : & Queue ,
517
+ mem : & SingleRegionGuestMemory ,
518
+ ) -> Result < u16 , vm_memory:: GuestMemoryError > {
519
+ let addr =
520
+ queue
521
+ . used_ring
522
+ . checked_add ( 2 )
523
+ . ok_or ( vm_memory:: GuestMemoryError :: InvalidGuestAddress (
524
+ queue. used_ring ,
525
+ ) ) ?;
526
+ let val = mem. load ( addr, Ordering :: Acquire ) ?;
527
+ Ok ( u16:: from_le ( val) )
528
+ }
529
+
530
+ /// # Specification (VirtIO 1.3, Section 2.7.14: "Receiving Used Buffers From The Device")
531
+ ///
532
+ /// Kani proof harness for verifying the behavior of the `add_used` method of
533
+ /// the `Queue`. When the device has finished processing a buffer, it must add
534
+ /// an element to the used ring, indicating which descriptor chain was used and
535
+ /// how many bytes were written. The device must increment the used index after
536
+ /// writing the element. Additionally, for this implementation, we verify that
537
+ /// if the descriptor index is out of bounds, the operation must fail and the
538
+ /// used index must not be incremented. Note that this proof does not verify
539
+ /// Section 2.7.8.2: "Device Requirements: The Virtqueue Used Ring"
540
+ #[ kani:: proof]
541
+ #[ kani:: unwind( 0 ) ]
542
+ fn verify_add_used ( ) {
543
+ let ProofContext { mut queue, memory } = kani:: any ( ) ;
544
+ let used_idx = queue. next_used ;
545
+ let used_desc_table_index = kani:: any ( ) ;
546
+ let old_val = get_used_idx ( & queue, & memory) . unwrap ( ) ;
547
+ let old_num_added = queue. num_added ;
548
+ if queue
549
+ . add_used ( & memory, used_desc_table_index, kani:: any ( ) )
550
+ . is_ok ( )
551
+ {
552
+ assert_eq ! ( queue. next_used, used_idx + Wrapping ( 1 ) ) ;
553
+ assert_eq ! ( queue. next_used. 0 , get_used_idx( & queue, & memory) . unwrap( ) ) ;
554
+ assert_eq ! ( old_num_added + Wrapping ( 1 ) , queue. num_added) ;
555
+ kani:: cover!( ) ;
556
+ } else {
557
+ // On failure, we expect the next_used to remain unchanged
558
+ // and the used_desc_table_index to be out of bounds.
559
+ assert_eq ! ( queue. next_used, used_idx) ;
560
+ assert ! ( used_desc_table_index >= queue. size( ) ) ;
561
+ // The old value should still be the same as before the add_used call.
562
+ assert_eq ! ( old_val, get_used_idx( & queue, & memory) . unwrap( ) ) ;
563
+ // No change in num_added field.
564
+ assert_eq ! ( old_num_added, queue. num_added) ;
565
+ kani:: cover!( ) ;
566
+ }
567
+ }
0 commit comments