@@ -229,10 +229,8 @@ impl VirtAddr {
229
229
pub ( crate ) fn steps_between_impl ( start : & Self , end : & Self ) -> Option < usize > {
230
230
let mut steps = end. 0 . checked_sub ( start. 0 ) ?;
231
231
232
- // Check if we jumped the gap.
233
- if end. 0 . get_bit ( 47 ) && !start. 0 . get_bit ( 47 ) {
234
- steps = steps. checked_sub ( 0xffff_0000_0000_0000 ) . unwrap ( ) ;
235
- }
232
+ // Mask away extra bits that appear while jumping the gap.
233
+ steps &= 0xffff_ffff_ffff ;
236
234
237
235
usize:: try_from ( steps) . ok ( )
238
236
}
@@ -258,7 +256,7 @@ impl VirtAddr {
258
256
_ => { }
259
257
}
260
258
261
- Some ( Self :: new ( addr) )
259
+ Some ( unsafe { Self :: new_unsafe ( addr) } )
262
260
}
263
261
}
264
262
@@ -373,7 +371,7 @@ impl Step for VirtAddr {
373
371
_ => { }
374
372
}
375
373
376
- Some ( Self :: new ( addr) )
374
+ Some ( unsafe { Self :: new_unsafe ( addr) } )
377
375
}
378
376
}
379
377
@@ -784,3 +782,164 @@ mod tests {
784
782
assert_eq ! ( VirtAddr :: from_ptr( slice) , VirtAddr :: from_ptr( & slice[ 0 ] ) ) ;
785
783
}
786
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
+ // Manually calculate the expected address after stepping once.
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
+ if let Some ( expected) = expected {
822
+ // Verify that `expected` is a valid address.
823
+ assert ! ( VirtAddr :: try_new( expected) . is_ok( ) ) ;
824
+ }
825
+ // Verify `forward_checked`.
826
+ let next = Step :: forward_checked ( start, 1 ) ;
827
+ assert ! ( next. map( VirtAddr :: as_u64) == expected) ;
828
+ }
829
+
830
+ // This harness proves that the result of taking two small steps is the
831
+ // same as taking one combined large step.
832
+ #[ kani:: proof]
833
+ fn forward_induction_step ( ) {
834
+ let start_raw: u64 = kani:: any ( ) ;
835
+ let Ok ( start) = VirtAddr :: try_new ( start_raw) else {
836
+ return ;
837
+ } ;
838
+
839
+ let count1: usize = kani:: any ( ) ;
840
+ let count2: usize = kani:: any ( ) ;
841
+ // If we can take two small steps...
842
+ let Some ( next1) = Step :: forward_checked ( start, count1) else {
843
+ return ;
844
+ } ;
845
+ let Some ( next2) = Step :: forward_checked ( next1, count2) else {
846
+ return ;
847
+ } ;
848
+
849
+ // ...then we can also take one combined large step.
850
+ let count_both = count1 + count2;
851
+ let next_both = Step :: forward ( start, count_both) ;
852
+ assert ! ( next2 == next_both) ;
853
+ }
854
+
855
+ // The next two proof harnesses prove the correctness of the `backward`
856
+ // implementation of VirtAddr using the `forward` implementation which
857
+ // we've already proven to be correct.
858
+ // They do this by proving the symmetry between those two functions.
859
+
860
+ // This harness proves the correctness of the implementation of `backward`
861
+ // for all inputs for which `forward_checked` succeeds.
862
+ #[ kani:: proof]
863
+ fn forward_implies_backward ( ) {
864
+ let start_raw: u64 = kani:: any ( ) ;
865
+ let Ok ( start) = VirtAddr :: try_new ( start_raw) else {
866
+ return ;
867
+ } ;
868
+ let count: usize = kani:: any ( ) ;
869
+
870
+ // If `forward_checked` succeeds...
871
+ let Some ( end) = Step :: forward_checked ( start, count) else {
872
+ return ;
873
+ } ;
874
+
875
+ // ...then `backward` succeeds as well.
876
+ let start2 = Step :: backward ( end, count) ;
877
+ assert ! ( start == start2) ;
878
+ }
879
+
880
+ // This harness proves that for all inputs for which `backward_checked`
881
+ // succeeds, `forward` succeeds as well.
882
+ #[ kani:: proof]
883
+ fn backward_implies_forward ( ) {
884
+ let end_raw: u64 = kani:: any ( ) ;
885
+ let Ok ( end) = VirtAddr :: try_new ( end_raw) else {
886
+ return ;
887
+ } ;
888
+ let count: usize = kani:: any ( ) ;
889
+
890
+ // If `backward_checked` succeeds...
891
+ let Some ( start) = Step :: backward_checked ( end, count) else {
892
+ return ;
893
+ } ;
894
+
895
+ // ...then `forward` succeeds as well.
896
+ let end2 = Step :: forward ( start, count) ;
897
+ assert ! ( end == end2) ;
898
+ }
899
+
900
+ // The next two proof harnesses prove the correctness of the
901
+ // `steps_between` implementation of VirtAddr using the `forward`
902
+ // implementation which we've already proven to be correct.
903
+ // They do this by proving the symmetry between those two functions.
904
+
905
+ // This harness proves the correctness of the implementation of
906
+ // `steps_between` for all inputs for which `forward_checked` succeeds.
907
+ #[ kani:: proof]
908
+ fn forward_implies_steps_between ( ) {
909
+ let start: u64 = kani:: any ( ) ;
910
+ let Ok ( start) = VirtAddr :: try_new ( start) else {
911
+ return ;
912
+ } ;
913
+ let count: usize = kani:: any ( ) ;
914
+
915
+ // If `forward_checked` succeeds...
916
+ let Some ( end) = Step :: forward_checked ( start, count) else {
917
+ return ;
918
+ } ;
919
+
920
+ // ...then `steps_between` succeeds as well.
921
+ assert ! ( Step :: steps_between( & start, & end) == Some ( count) ) ;
922
+ }
923
+
924
+ // This harness proves that for all inputs for which `steps_between`
925
+ // succeeds, `forward` succeeds as well.
926
+ #[ kani:: proof]
927
+ fn steps_between_implies_forward ( ) {
928
+ let start: u64 = kani:: any ( ) ;
929
+ let Ok ( start) = VirtAddr :: try_new ( start) else {
930
+ return ;
931
+ } ;
932
+ let end: u64 = kani:: any ( ) ;
933
+ let Ok ( end) = VirtAddr :: try_new ( end) else {
934
+ return ;
935
+ } ;
936
+
937
+ // If `steps_between` succeeds...
938
+ let Some ( count) = Step :: steps_between ( & start, & end) else {
939
+ return ;
940
+ } ;
941
+
942
+ // ...then `forward` succeeds as well.
943
+ assert ! ( Step :: forward( start, count) == end) ;
944
+ }
945
+ }
0 commit comments