@@ -826,15 +826,6 @@ mod verification {
826
826
)
827
827
}
828
828
829
- fn setup_zeroed_guest_memory ( ) -> ProofGuestMemory {
830
- guest_memory ( unsafe {
831
- std:: alloc:: alloc_zeroed ( std:: alloc:: Layout :: from_size_align_unchecked (
832
- GUEST_MEMORY_SIZE ,
833
- 16 ,
834
- ) )
835
- } )
836
- }
837
-
838
829
// Constants describing the in-memory layout of a queue of size FIRECRACKER_MAX_SIZE starting
839
830
// at the beginning of guest memory. These are based on Section 2.6 of the VirtIO 1.1
840
831
// specification.
@@ -882,27 +873,15 @@ mod verification {
882
873
883
874
ProofContext ( queue, mem)
884
875
}
885
-
886
- /// Creates a [`ProofContext`] where the queue layout is fixed to a valid one and where
887
- /// guest memory is initialized to all zeros.
888
- pub fn bounded ( ) -> Self {
889
- let mem = setup_zeroed_guest_memory ( ) ;
890
- let mut queue = less_arbitrary_queue ( ) ;
891
- queue. initialize ( & mem) . unwrap ( ) ;
892
-
893
- assert ! ( queue. is_valid( & mem) ) ;
894
-
895
- ProofContext ( queue, mem)
896
- }
897
876
}
898
877
899
878
impl kani:: Arbitrary for ProofContext {
900
879
fn any ( ) -> Self {
901
880
let mem = setup_kani_guest_memory ( ) ;
902
881
let mut queue: Queue = kani:: any ( ) ;
903
- queue. initialize ( & mem) . unwrap ( ) ;
904
882
905
883
kani:: assume ( queue. is_valid ( & mem) ) ;
884
+ queue. initialize ( & mem) . unwrap ( ) ;
906
885
907
886
ProofContext ( queue, mem)
908
887
}
@@ -950,7 +929,7 @@ mod verification {
950
929
// has been processed. This is done by the driver
951
930
// defining a "used_event" index, which tells the device "please do not notify me until
952
931
// used.ring[used_event] has been written to by you".
953
- let ProofContext ( mut queue, _) = ProofContext :: bounded_queue ( ) ;
932
+ let ProofContext ( mut queue, _) = kani :: any ( ) ;
954
933
955
934
let num_added_old = queue. num_added . 0 ;
956
935
let needs_notification = queue. prepare_kick ( ) ;
@@ -991,7 +970,7 @@ mod verification {
991
970
// number of added descriptors being counted in Queue.num_added), and then use
992
971
// "prepare_kick" to check if any of those descriptors should have triggered a
993
972
// notification.
994
- let ProofContext ( mut queue, _) = ProofContext :: bounded_queue ( ) ;
973
+ let ProofContext ( mut queue, _) = kani :: any ( ) ;
995
974
996
975
queue. enable_notif_suppression ( ) ;
997
976
assert ! ( queue. uses_notif_suppression) ;
@@ -1022,10 +1001,46 @@ mod verification {
1022
1001
assert_eq ! ( queue. prepare_kick( ) , needs_notification) ;
1023
1002
}
1024
1003
1004
+ #[ kani:: proof]
1005
+ #[ kani:: unwind( 0 ) ]
1006
+ fn verify_add_used ( ) {
1007
+ let ProofContext ( mut queue, _) = kani:: any ( ) ;
1008
+
1009
+ // The spec here says (2.6.8.2):
1010
+ //
1011
+ // The device MUST set len prior to updating the used idx.
1012
+ // The device MUST write at least len bytes to descriptor, beginning at the first
1013
+ // device-writable buffer, prior to updating the used idx.
1014
+ // The device MAY write more than len bytes to descriptor.
1015
+ //
1016
+ // We can't really verify any of these. We can verify that guest memory is updated correctly
1017
+ // though
1018
+
1019
+ // index into used ring at which the index of the descriptor to which
1020
+ // the device wrote.
1021
+ let used_idx = queue. next_used ;
1022
+
1023
+ let used_desc_table_index = kani:: any ( ) ;
1024
+ if queue. add_used ( used_desc_table_index, kani:: any ( ) ) . is_ok ( ) {
1025
+ assert_eq ! ( queue. next_used, used_idx + Wrapping ( 1 ) ) ;
1026
+ } else {
1027
+ assert_eq ! ( queue. next_used, used_idx) ;
1028
+
1029
+ // Ideally, here we would want to actually read the relevant values from memory and
1030
+ // assert they are unchanged. However, kani will run out of memory if we try to do so,
1031
+ // so we instead verify the following "proxy property": If an error happened, then
1032
+ // it happened at the very beginning of add_used, meaning no memory accesses were
1033
+ // done. This is relying on implementation details of add_used, namely that
1034
+ // the check for out-of-bounds descriptor index happens at the very beginning of the
1035
+ // function.
1036
+ assert ! ( used_desc_table_index >= queue. actual_size( ) ) ;
1037
+ }
1038
+ }
1039
+
1025
1040
#[ kani:: proof]
1026
1041
#[ kani:: unwind( 0 ) ]
1027
1042
fn verify_is_empty ( ) {
1028
- let ProofContext ( queue, _) = ProofContext :: bounded_queue ( ) ;
1043
+ let ProofContext ( queue, _) = kani :: any ( ) ;
1029
1044
1030
1045
assert_eq ! ( queue. len( ) == 0 , queue. is_empty( ) ) ;
1031
1046
}
@@ -1120,7 +1135,7 @@ mod verification {
1120
1135
#[ kani:: unwind( 0 ) ]
1121
1136
#[ kani:: solver( cadical) ]
1122
1137
fn verify_pop ( ) {
1123
- let ProofContext ( mut queue, _) = ProofContext :: bounded_queue ( ) ;
1138
+ let ProofContext ( mut queue, _) = kani :: any ( ) ;
1124
1139
1125
1140
// This is an assertion in pop which we use to abort firecracker in a ddos scenario
1126
1141
// This condition being false means that the guest is asking us to process every element
@@ -1143,7 +1158,7 @@ mod verification {
1143
1158
#[ kani:: unwind( 0 ) ]
1144
1159
#[ kani:: solver( cadical) ]
1145
1160
fn verify_undo_pop ( ) {
1146
- let ProofContext ( mut queue, _) = ProofContext :: bounded_queue ( ) ;
1161
+ let ProofContext ( mut queue, _) = kani :: any ( ) ;
1147
1162
1148
1163
// See verify_pop for explanation
1149
1164
kani:: assume ( queue. len ( ) <= queue. actual_size ( ) ) ;
@@ -1177,7 +1192,7 @@ mod verification {
1177
1192
#[ kani:: unwind( 0 ) ]
1178
1193
#[ kani:: solver( cadical) ]
1179
1194
fn verify_checked_new ( ) {
1180
- let ProofContext ( queue, mem) = ProofContext :: bounded_queue ( ) ;
1195
+ let ProofContext ( queue, mem) = kani :: any ( ) ;
1181
1196
1182
1197
let index = kani:: any ( ) ;
1183
1198
let maybe_chain =
0 commit comments