@@ -565,3 +565,139 @@ fn verify_add_used() {
565
565
kani:: cover!( ) ;
566
566
}
567
567
}
568
+
569
+ /// This proof checks that after setting the `next_used` field of the queue
570
+ /// using `set_next_used(x)`, reading back the value of `next_used` returns the
571
+ /// same value `x`.
572
+ #[ kani:: proof]
573
+ #[ kani:: unwind( 0 ) ]
574
+ fn verify_used_ring_avail_event ( ) {
575
+ let ProofContext {
576
+ mut queue,
577
+ memory : _,
578
+ } = kani:: any ( ) ;
579
+ let x = kani:: any ( ) ;
580
+ queue. set_next_used ( x) ;
581
+ assert_eq ! ( x, queue. next_used. 0 ) ;
582
+ }
583
+
584
+ /// # Specification (VirtIO 1.3, Section 2.7.6.1: "Driver Requirements: The Virtqueue Available Ring")
585
+ ///
586
+ /// This proof checks that:
587
+ /// - If there are pending entries in the avail ring (avail_idx != next_avail),
588
+ /// `enable_notification` returns true.
589
+ /// - If there are no pending entries (avail_idx == next_avail), it returns false.
590
+ /// This matches the monotonicity property of the avail ring in VirtIO 1.3 Section 2.7.6.1.
591
+ #[ kani:: proof]
592
+ #[ kani:: unwind( 0 ) ]
593
+ fn verify_enable_driver_to_device_notification ( ) {
594
+ let ProofContext { mut queue, memory } = kani:: any ( ) ;
595
+
596
+ // The enable_notification method sets notification to true and returns
597
+ // - true, if there are pending entries in the `idx` field of the
598
+ // avail ring
599
+ // - false, if there are no pending entries in the `idx` field of the
600
+ // avail ring The check for pending entries is done by comparing the
601
+ // current `avail_idx` with the `next_avail` field of the queue. If they
602
+ // are different, there are pending entries, otherwise there are no
603
+ // pending entries. The equality check is a consequence of the
604
+ // monotonicity property of `idx` (2.7.6.1) that it cannot be decreased
605
+ // by the driver.
606
+ if queue. enable_notification ( & memory) . unwrap ( ) {
607
+ assert_ne ! (
608
+ queue. avail_idx( & memory, Ordering :: Relaxed ) . unwrap( ) ,
609
+ queue. next_avail
610
+ ) ;
611
+ } else {
612
+ assert_eq ! (
613
+ queue. avail_idx( & memory, Ordering :: Relaxed ) . unwrap( ) ,
614
+ queue. next_avail
615
+ ) ;
616
+ }
617
+ }
618
+
619
+ // Helper method that reads `val` from the `avail_event` field of the used ring, using
620
+ // the provided ordering. Takes used_ring address and queue size directly.
621
+ fn get_avail_event < M : GuestMemory > (
622
+ used_ring_addr : GuestAddress ,
623
+ queue_size : u16 ,
624
+ mem : & M ,
625
+ order : Ordering ,
626
+ ) -> Result < u16 , Error > {
627
+ // This can not overflow an u64 since it is working with relatively small numbers compar
628
+ // to u64::MAX.
629
+ let avail_event_offset =
630
+ VIRTQ_USED_RING_HEADER_SIZE + VIRTQ_USED_ELEMENT_SIZE * u64:: from ( queue_size) ;
631
+ let addr = used_ring_addr
632
+ . checked_add ( avail_event_offset)
633
+ . ok_or ( Error :: AddressOverflow ) ?;
634
+
635
+ mem. load :: < u16 > ( addr, order) . map_err ( Error :: GuestMemory )
636
+ }
637
+
638
+ // Get the value of the `flags` field of the used ring, applying the specified ordering.
639
+ fn get_used_flags < M : GuestMemory > ( queue : & Queue , mem : & M , order : Ordering ) -> Result < u16 , Error > {
640
+ mem. load :: < u16 > ( queue. used_ring , order)
641
+ . map ( u16:: from_le)
642
+ . map_err ( Error :: GuestMemory )
643
+ }
644
+ /// # Specification (VirtIO 1.3, Section 2.7.7: "Used Buffer Notification Suppression")
645
+ ///
646
+ /// This proof checks:
647
+ /// - If event_idx is not enabled, `set_notification(false)` sets used.flags to 1 (NO_NOTIFY).
648
+ /// - If event_idx is enabled, the call is a no-op.
649
+ #[ kani:: proof]
650
+ #[ kani:: unwind( 0 ) ]
651
+ fn verify_set_notification_true ( ) {
652
+ let ProofContext { mut queue, memory } = kani:: any ( ) ;
653
+ if queue
654
+ . set_notification ( & memory, true /* enable notification */ )
655
+ . is_ok ( )
656
+ {
657
+ if queue. event_idx_enabled {
658
+ // Since VIRTIO_F_EVENT_IDX is negotiated, we make sure that set_notification
659
+ // has updated the used.avail_event field with the tail position of
660
+ // the avail ring.
661
+ let used_ring_addr = queue. used_ring ;
662
+ let queue_size = queue. size ( ) ;
663
+ kani:: cover!( ) ;
664
+ assert_eq ! (
665
+ get_avail_event( used_ring_addr, queue_size, & memory, Ordering :: Relaxed ) . unwrap( ) ,
666
+ queue. next_avail. 0
667
+ ) ;
668
+ } else {
669
+ // If VIRTIO_F_EVENT_IDX is not negotiated, we make sure that the
670
+ // used.flags field is set to 0, meaning that the driver should not
671
+ // send notifications to the device.
672
+ kani:: cover!( ) ;
673
+ assert_eq ! (
674
+ get_used_flags( & queue, & memory, Ordering :: Relaxed ) . unwrap( ) ,
675
+ 0
676
+ ) ;
677
+ }
678
+ }
679
+ }
680
+
681
+ /// # Specification (VirtIO 1.3, Section 2.7.7: "Used Buffer Notification Suppression")
682
+ ///
683
+ /// This proof checks:
684
+ /// - If event_idx is not enabled, `set_notification(false)` sets used.flags to 1 (NO_NOTIFY).
685
+ /// - If event_idx is enabled, the call is a no-op.
686
+ #[ kani:: proof]
687
+ #[ kani:: unwind( 0 ) ]
688
+ fn verify_set_notification_false ( ) {
689
+ let ProofContext { mut queue, memory } = kani:: any ( ) ;
690
+ let result = queue. set_notification ( & memory, false /* disable notification */ ) ;
691
+ if !queue. event_idx_enabled {
692
+ // Check for Sec 2.7.10
693
+ assert_eq ! (
694
+ get_used_flags( & queue, & memory, Ordering :: Relaxed ) . unwrap( ) ,
695
+ 1
696
+ ) ;
697
+ // don't check Ok() result since that is a property of the
698
+ // underlying mem system and out of scope. E.g., it is stubbed for
699
+ // this proof and we always expect it to succeed.
700
+ } else {
701
+ assert ! ( result. is_ok( ) ) ;
702
+ }
703
+ }
0 commit comments