@@ -793,6 +793,7 @@ mod tests {
793
793
}
794
794
795
795
#[ cfg( kani) ]
796
+ #[ allow( dead_code) ] // Avoid warning when using stubs
796
797
mod verification {
797
798
use std:: mem:: ManuallyDrop ;
798
799
@@ -801,7 +802,9 @@ mod verification {
801
802
use vm_memory:: VolatileSlice ;
802
803
803
804
use super :: { IoVecBuffer , IoVecBufferMut } ;
805
+ use crate :: arch:: PAGE_SIZE ;
804
806
use crate :: devices:: virtio:: iov_deque:: IovDeque ;
807
+ use crate :: devices:: virtio:: queue:: FIRECRACKER_MAX_QUEUE_SIZE ;
805
808
806
809
// Maximum memory size to use for our buffers. For the time being 1KB.
807
810
const GUEST_MEMORY_SIZE : usize = 1 << 10 ;
@@ -813,6 +816,50 @@ mod verification {
813
816
// >= 1.
814
817
const MAX_DESC_LENGTH : usize = 4 ;
815
818
819
+ mod stubs {
820
+ use super :: * ;
821
+
822
+ /// This is a stub for the `IovDeque::push_back` method.
823
+ ///
824
+ /// `IovDeque` relies on a special allocation of two pages of virtual memory, where both of
825
+ /// these point to the same underlying physical page. This way, the contents of the first
826
+ /// page of virtual memory are automatically mirrored in the second virtual page. We do
827
+ /// that in order to always have the elements that are currently in the ring buffer in
828
+ /// consecutive (virtual) memory.
829
+ ///
830
+ /// To build this particular memory layout we create a new `memfd` object, allocate memory
831
+ /// with `mmap` and call `mmap` again to make sure both pages point to the page allocated
832
+ /// via the `memfd` object. These ffi calls make kani complain, so here we mock the
833
+ /// `IovDeque` object memory with a normal memory allocation of two pages worth of data.
834
+ ///
835
+ /// This stub helps imitate the effect of mirroring without all the elaborate memory
836
+ /// allocation trick.
837
+ pub fn push_back ( deque : & mut IovDeque , iov : iovec ) {
838
+ // This should NEVER happen, since our ring buffer is as big as the maximum queue size.
839
+ // We also check for the sanity of the VirtIO queues, in queue.rs, which means that if
840
+ // we ever try to add something in a full ring buffer, there is an internal
841
+ // bug in the device emulation logic. Panic here because the device is
842
+ // hopelessly broken.
843
+ assert ! (
844
+ !deque. is_full( ) ,
845
+ "The number of `iovec` objects is bigger than the available space"
846
+ ) ;
847
+
848
+ let offset = ( deque. start + deque. len ) as usize ;
849
+ let mirror = if offset >= FIRECRACKER_MAX_QUEUE_SIZE as usize {
850
+ offset - FIRECRACKER_MAX_QUEUE_SIZE as usize
851
+ } else {
852
+ offset + FIRECRACKER_MAX_QUEUE_SIZE as usize
853
+ } ;
854
+
855
+ // SAFETY: self.iov is a valid pointer and `self.start + self.len` is within range (we
856
+ // asserted before that the buffer is not full).
857
+ unsafe { deque. iov . add ( offset) . write_volatile ( iov) } ;
858
+ unsafe { deque. iov . add ( mirror) . write_volatile ( iov) } ;
859
+ deque. len += 1 ;
860
+ }
861
+ }
862
+
816
863
fn create_iovecs ( mem : * mut u8 , size : usize , nr_descs : usize ) -> ( Vec < iovec > , u32 ) {
817
864
let mut vecs: Vec < iovec > = Vec :: with_capacity ( nr_descs) ;
818
865
let mut len = 0u32 ;
@@ -843,8 +890,23 @@ mod verification {
843
890
}
844
891
}
845
892
893
+ fn create_iov_deque ( ) -> IovDeque {
894
+ // SAFETY: safe because the layout has non-zero size
895
+ let mem = unsafe {
896
+ std:: alloc:: alloc ( std:: alloc:: Layout :: from_size_align_unchecked (
897
+ 2 * PAGE_SIZE ,
898
+ PAGE_SIZE ,
899
+ ) )
900
+ } ;
901
+ IovDeque {
902
+ iov : mem. cast ( ) ,
903
+ start : kani:: any_where ( |& start| start < FIRECRACKER_MAX_QUEUE_SIZE ) ,
904
+ len : 0 ,
905
+ }
906
+ }
907
+
846
908
fn create_iovecs_mut ( mem : * mut u8 , size : usize , nr_descs : usize ) -> ( IovDeque , u32 ) {
847
- let mut vecs = IovDeque :: new ( ) . unwrap ( ) ;
909
+ let mut vecs = create_iov_deque ( ) ;
848
910
let mut len = 0u32 ;
849
911
for _ in 0 ..nr_descs {
850
912
// The `IoVecBufferMut` constructors ensure that the memory region described by every
@@ -953,6 +1015,7 @@ mod verification {
953
1015
#[ kani:: proof]
954
1016
#[ kani:: unwind( 5 ) ]
955
1017
#[ kani:: solver( cadical) ]
1018
+ #[ kani:: stub( IovDeque :: push_back, stubs:: push_back) ]
956
1019
fn verify_write_to_iovec ( ) {
957
1020
for nr_descs in 0 ..MAX_DESC_LENGTH {
958
1021
let mut iov_mut = IoVecBufferMut :: any_of_length ( nr_descs) ;
@@ -981,6 +1044,7 @@ mod verification {
981
1044
. unwrap( ) ,
982
1045
buf. len( ) . min( iov_mut. len( ) . saturating_sub( offset) as usize )
983
1046
) ;
1047
+ std:: mem:: forget ( iov_mut. vecs ) ;
984
1048
}
985
1049
}
986
1050
}
0 commit comments