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