@@ -796,6 +796,7 @@ mod tests {
796
796
}
797
797
798
798
#[ cfg( kani) ]
799
+ #[ allow( dead_code) ] // Avoid warning when using stubs
799
800
mod verification {
800
801
use std:: mem:: ManuallyDrop ;
801
802
@@ -804,7 +805,9 @@ mod verification {
804
805
use vm_memory:: VolatileSlice ;
805
806
806
807
use super :: { IoVecBuffer , IoVecBufferMut } ;
808
+ use crate :: arch:: PAGE_SIZE ;
807
809
use crate :: devices:: virtio:: iov_deque:: IovDeque ;
810
+ use crate :: devices:: virtio:: queue:: FIRECRACKER_MAX_QUEUE_SIZE ;
808
811
809
812
// Maximum memory size to use for our buffers. For the time being 1KB.
810
813
const GUEST_MEMORY_SIZE : usize = 1 << 10 ;
@@ -816,6 +819,50 @@ mod verification {
816
819
// >= 1.
817
820
const MAX_DESC_LENGTH : usize = 4 ;
818
821
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
+
819
866
fn create_iovecs ( mem : * mut u8 , size : usize , nr_descs : usize ) -> ( Vec < iovec > , u32 ) {
820
867
let mut vecs: Vec < iovec > = Vec :: with_capacity ( nr_descs) ;
821
868
let mut len = 0u32 ;
@@ -846,8 +893,23 @@ mod verification {
846
893
}
847
894
}
848
895
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
+
849
911
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 ( ) ;
851
913
let mut len = 0u32 ;
852
914
for _ in 0 ..nr_descs {
853
915
// The `IoVecBufferMut` constructors ensure that the memory region described by every
@@ -956,6 +1018,7 @@ mod verification {
956
1018
#[ kani:: proof]
957
1019
#[ kani:: unwind( 5 ) ]
958
1020
#[ kani:: solver( cadical) ]
1021
+ #[ kani:: stub( IovDeque :: push_back, stubs:: push_back) ]
959
1022
fn verify_write_to_iovec ( ) {
960
1023
for nr_descs in 0 ..MAX_DESC_LENGTH {
961
1024
let mut iov_mut = IoVecBufferMut :: any_of_length ( nr_descs) ;
@@ -984,6 +1047,7 @@ mod verification {
984
1047
. unwrap( ) ,
985
1048
buf. len( ) . min( iov_mut. len( ) . saturating_sub( offset) as usize )
986
1049
) ;
1050
+ std:: mem:: forget ( iov_mut. vecs ) ;
987
1051
}
988
1052
}
989
1053
}
0 commit comments