@@ -782,3 +782,159 @@ mod tests {
782
782
assert_eq ! ( VirtAddr :: from_ptr( slice) , VirtAddr :: from_ptr( & slice[ 0 ] ) ) ;
783
783
}
784
784
}
785
+
786
+ #[ cfg( kani) ]
787
+ mod proofs {
788
+ use super :: * ;
789
+
790
+ // The next two proof harnesses prove the correctness of the `forward`
791
+ // implementation of VirtAddr.
792
+
793
+ // This harness proves that our implementation can correctly take 0 or 1
794
+ // step starting from any address.
795
+ #[ kani:: proof]
796
+ fn forward_base_case ( ) {
797
+ let start_raw: u64 = kani:: any ( ) ;
798
+ let Ok ( start) = VirtAddr :: try_new ( start_raw) else {
799
+ return ;
800
+ } ;
801
+
802
+ // Adding 0 to any address should always yield the same address.
803
+ let same = Step :: forward ( start, 0 ) ;
804
+ assert ! ( start == same) ;
805
+
806
+ // Verify that we can add 1 to any address.
807
+ let expected = match start_raw {
808
+ // Adding 1 to addresses in this range don't require gap jumps, so
809
+ // we can just add 1.
810
+ 0x0000_0000_0000_0000 ..=0x0000_7fff_ffff_fffe => Some ( start_raw + 1 ) ,
811
+ // Adding 1 to this address jumps the gap.
812
+ 0x0000_7fff_ffff_ffff => Some ( 0xffff_8000_0000_0000 ) ,
813
+ // The range of non-canonical addresses.
814
+ 0x0000_8000_0000_0000 ..=0xffff_7fff_ffff_ffff => unreachable ! ( ) ,
815
+ // Adding 1 to addresses in this range don't require gap jumps, so
816
+ // we can just add 1.
817
+ 0xffff_8000_0000_0000 ..=0xffff_ffff_ffff_fffe => Some ( start_raw + 1 ) ,
818
+ // Adding 1 to this address causes an overflow.
819
+ 0xffff_ffff_ffff_ffff => None ,
820
+ } ;
821
+ let next = Step :: forward_checked ( start, 1 ) ;
822
+ assert ! ( next. map( VirtAddr :: as_u64) == expected) ;
823
+ }
824
+
825
+ // This harness proves that the result of taking two small steps is the
826
+ // same as taking one combined large step.
827
+ #[ kani:: proof]
828
+ fn forward_induction_step ( ) {
829
+ let start_raw: u64 = kani:: any ( ) ;
830
+ let Ok ( start) = VirtAddr :: try_new ( start_raw) else {
831
+ return ;
832
+ } ;
833
+
834
+ let count1: usize = kani:: any ( ) ;
835
+ let count2: usize = kani:: any ( ) ;
836
+ // If we can take two small steps...
837
+ let Some ( next1) = Step :: forward_checked ( start, count1) else {
838
+ return ;
839
+ } ;
840
+ let Some ( next2) = Step :: forward_checked ( next1, count2) else {
841
+ return ;
842
+ } ;
843
+
844
+ // ...then we can also take one combined large step.
845
+ let count_both = count1 + count2;
846
+ let next_both = Step :: forward ( start, count_both) ;
847
+ assert ! ( next2 == next_both) ;
848
+ }
849
+
850
+ // The next two proof harnesses prove the correctness of the `backward`
851
+ // implementation of VirtAddr using the `forward` implementation which
852
+ // we've already proven to be correct.
853
+ // They do this by proving the symmetry between those two functions.
854
+
855
+ // This harness proves the correctness of the implementation of `backward`
856
+ // for all inputs for which `forward_checked` succeeds.
857
+ #[ kani:: proof]
858
+ fn forward_implies_backward ( ) {
859
+ let start_raw: u64 = kani:: any ( ) ;
860
+ let Ok ( start) = VirtAddr :: try_new ( start_raw) else {
861
+ return ;
862
+ } ;
863
+ let count: usize = kani:: any ( ) ;
864
+
865
+ // If `forward_checked` succeeds...
866
+ let Some ( end) = Step :: forward_checked ( start, count) else {
867
+ return ;
868
+ } ;
869
+
870
+ // ...then `backward` succeeds as well.
871
+ let start2 = Step :: backward ( end, count) ;
872
+ assert ! ( start == start2) ;
873
+ }
874
+
875
+ // This harness proves that for all inputs for which `backward_checked`
876
+ // succeeds, `forward` succeeds as well.
877
+ #[ kani:: proof]
878
+ fn backward_implies_forward ( ) {
879
+ let end_raw: u64 = kani:: any ( ) ;
880
+ let Ok ( end) = VirtAddr :: try_new ( end_raw) else {
881
+ return ;
882
+ } ;
883
+ let count: usize = kani:: any ( ) ;
884
+
885
+ // If `backward_checked` succeeds...
886
+ let Some ( start) = Step :: backward_checked ( end, count) else {
887
+ return ;
888
+ } ;
889
+
890
+ // ...then `forward` succeeds as well.
891
+ let end2 = Step :: forward ( start, count) ;
892
+ assert ! ( end == end2) ;
893
+ }
894
+
895
+ // The next two proof harnesses prove the correctness of the
896
+ // `steps_between` implementation of VirtAddr using the `forward`
897
+ // implementation which we've already proven to be correct.
898
+ // They do this by proving the symmetry between those two functions.
899
+
900
+ // This harness proves the correctness of the implementation of
901
+ // `steps_between` for all inputs for which `forward_checked` succeeds.
902
+ #[ kani:: proof]
903
+ fn forward_implies_steps_between ( ) {
904
+ let start: u64 = kani:: any ( ) ;
905
+ let Ok ( start) = VirtAddr :: try_new ( start) else {
906
+ return ;
907
+ } ;
908
+ let count: usize = kani:: any ( ) ;
909
+
910
+ // If `forward_checked` succeeds...
911
+ let Some ( end) = Step :: forward_checked ( start, count) else {
912
+ return ;
913
+ } ;
914
+
915
+ // ...then `steps_between` succeeds as well.
916
+ assert ! ( Step :: steps_between( & start, & end) == Some ( count) ) ;
917
+ }
918
+
919
+ // This harness proves that for all inputs for which `steps_between`
920
+ // succeeds, `forward` succeeds as well.
921
+ #[ kani:: proof]
922
+ fn steps_between_implies_forward ( ) {
923
+ let start: u64 = kani:: any ( ) ;
924
+ let Ok ( start) = VirtAddr :: try_new ( start) else {
925
+ return ;
926
+ } ;
927
+ let end: u64 = kani:: any ( ) ;
928
+ let Ok ( end) = VirtAddr :: try_new ( end) else {
929
+ return ;
930
+ } ;
931
+
932
+ // If `steps_between` succeeds...
933
+ let Some ( count) = Step :: steps_between ( & start, & end) else {
934
+ return ;
935
+ } ;
936
+
937
+ // ...then `forward` succeeds as well.
938
+ assert ! ( Step :: forward( start, count) == end) ;
939
+ }
940
+ }
0 commit comments