@@ -510,3 +510,58 @@ fn verify_device_notification_suppression() {
510510 // So we do not care
511511 }
512512}
513+
514+ /// Returns the `idx` field from the used ring in guest memory, as stored by [`Queue::add_used`].
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