@@ -78,7 +78,32 @@ mod _def {
78
78
/// address space.
79
79
/// 5. If `ptr`'s referent is not zero sized, then `A` is guaranteed to
80
80
/// live for at least `'a`.
81
+ #[ cfg_attr(
82
+ kani,
83
+ // TODO: Use `is_inbounds` once it's public. `can_write_unaligned`
84
+ // is technically stronger than we need.
85
+ kani:: requires( kani:: mem:: can_write_unaligned( ptr. as_ptr( ) ) ) ,
86
+ kani:: ensures( |p| kani:: mem:: can_write_unaligned( p. as_non_null( ) . as_ptr( ) ) ) ,
87
+ kani:: ensures( |p| {
88
+ #[ allow( ambiguous_wide_pointer_comparisons) ]
89
+ return p. as_non_null( ) == ptr;
90
+ } ) ,
91
+ kani:: ensures( |p| {
92
+ kani:: mem:: checked_size_of_raw( ptr. as_ptr( ) ) . unwrap( ) == 0 ||
93
+ kani:: mem:: same_allocation( p. as_non_null( ) . as_ptr( ) , ptr. as_ptr( ) )
94
+ } ) ,
95
+ ) ]
81
96
pub ( crate ) const unsafe fn new ( ptr : NonNull < T > ) -> PtrInner < ' a , T > {
97
+ #[ cfg( kani) ]
98
+ #[ kani:: proof_for_contract( PtrInner :: new) ]
99
+ fn proof ( ) {
100
+ fn new < T : ?Sized + KnownLayout > ( ) {
101
+ unsafe { PtrInner :: < T > :: new ( proofs:: any_nonnull ( ) ) } ;
102
+ }
103
+
104
+ for_many_types ! ( new) ;
105
+ }
106
+
82
107
// SAFETY: The caller has promised to satisfy all safety invariants
83
108
// of `PtrInner`.
84
109
Self { ptr, _marker : PhantomData }
@@ -146,6 +171,16 @@ impl<'a, T> PtrInner<'a, [T]> {
146
171
/// # Safety
147
172
///
148
173
/// `range` is a valid range (`start <= end`) and `end <= self.len()`.
174
+ #[ cfg_attr(
175
+ kani,
176
+ kani:: requires( range. start <= range. end && range. end <= self . len( ) ) ,
177
+ kani:: ensures( |r| r. len( ) == range. end - range. start) ,
178
+ kani:: ensures( |r| {
179
+ let begin = unsafe { r. as_non_null( ) . cast:: <T >( ) . sub( range. start) } ;
180
+ let ptr = self . as_non_null( ) . cast:: <T >( ) ;
181
+ begin == ptr
182
+ } )
183
+ ) ]
149
184
pub ( crate ) unsafe fn slice_unchecked ( self , range : Range < usize > ) -> Self {
150
185
let base = self . as_non_null ( ) . cast :: < T > ( ) . as_ptr ( ) ;
151
186
@@ -206,8 +241,23 @@ impl<'a, T> PtrInner<'a, [T]> {
206
241
///
207
242
/// The caller promises that `l_len <= self.len()`.
208
243
///
209
- /// Given `let (left, right) = ptr.split_at(l_len)`, it is guaranteed
210
- /// that `left` and `right` are contiguous and non-overlapping.
244
+ /// Given `let (left, right) = ptr.split_at(l_len)`, it is guaranteed that:
245
+ /// - `left.len() == l_len`
246
+ /// - `left` and `right` are contiguous and non-overlapping
247
+ /// - Together, `left` and `right` cover the exact same byte range as `self`
248
+ #[ cfg_attr(
249
+ kani,
250
+ kani:: requires( l_len <= self . len( ) ) ,
251
+ kani:: ensures( |( l, r) | l. len( ) == l_len && r. len( ) == self . len( ) - l_len) ,
252
+ kani:: ensures( |( l, _) | {
253
+ l. as_non_null( ) . cast:: <T >( ) == self . as_non_null( ) . cast:: <T >( )
254
+ } ) ,
255
+ kani:: ensures( |( l, r) | {
256
+ let l = l. as_non_null( ) . cast:: <T >( ) ;
257
+ let r = r. as_non_null( ) . cast:: <T >( ) ;
258
+ unsafe { l. add( l_len) == r }
259
+ } )
260
+ ) ]
211
261
pub ( crate ) unsafe fn split_at ( self , l_len : usize ) -> ( Self , Self ) {
212
262
// SAFETY: The caller promises that `l_len <= self.len()`.
213
263
// Trivially, `0 <= l_len`.
@@ -333,6 +383,11 @@ impl<'a, T, const N: usize> PtrInner<'a, [T; N]> {
333
383
/// Callers may assume that the returned `PtrInner` references the same
334
384
/// address and length as `self`.
335
385
#[ allow( clippy:: wrong_self_convention) ]
386
+ #[ cfg_attr(
387
+ kani,
388
+ kani:: ensures( |slc| slc. len( ) == N ) ,
389
+ kani:: ensures( |slc| self . as_non_null( ) . cast:: <T >( ) == slc. as_non_null( ) . cast:: <T >( ) )
390
+ ) ]
336
391
pub ( crate ) fn as_slice ( self ) -> PtrInner < ' a , [ T ] > {
337
392
let start = self . as_non_null ( ) . cast :: < T > ( ) . as_ptr ( ) ;
338
393
let slice = core:: ptr:: slice_from_raw_parts_mut ( start, N ) ;
@@ -395,6 +450,38 @@ impl<'a> PtrInner<'a, [u8]> {
395
450
/// - If this is a prefix cast, `ptr` has the same address as `self`.
396
451
/// - If this is a suffix cast, `remainder` has the same address as `self`.
397
452
#[ inline]
453
+ #[ cfg_attr(
454
+ kani,
455
+ kani:: ensures( |r| match r {
456
+ Ok ( ( t, rest) ) => {
457
+ let ptr_u8 = self . as_non_null( ) . cast:: <u8 >( ) ;
458
+ let t_u8 = t. as_non_null( ) . cast:: <u8 >( ) ;
459
+ let rest_u8 = rest. as_non_null( ) . cast:: <u8 >( ) ;
460
+ match cast_type {
461
+ CastType :: Prefix => {
462
+ t_u8 == ptr_u8
463
+ // TODO: Assert that `t` and `rest` are contiguous and
464
+ // non-overlapping.
465
+ }
466
+ CastType :: Suffix => {
467
+ // The second condition asserts that `rest` and `t` are
468
+ // contiguous and non-overlapping.
469
+ rest_u8 == ptr_u8 && unsafe { rest_u8. add( rest. len( ) ) == t_u8 }
470
+ }
471
+ }
472
+ }
473
+ Err ( CastError :: Alignment ( _) ) => {
474
+ true
475
+ // TODO: Prove that there would have been an alignment problem.
476
+ }
477
+ Err ( CastError :: Size ( _) ) => {
478
+ true
479
+ // TODO: Prove that there would have been a size problem.
480
+ }
481
+ // Unreachable because this error variant is uninhabited.
482
+ Err ( CastError :: Validity ( _) ) => false ,
483
+ } )
484
+ ) ]
398
485
pub ( crate ) fn try_cast_into < U > (
399
486
self ,
400
487
cast_type : CastType ,
@@ -519,3 +606,234 @@ mod tests {
519
606
}
520
607
}
521
608
}
609
+
610
+ #[ cfg( kani) ]
611
+ mod proofs {
612
+ use crate :: { SizeInfo , TrailingSliceLayout } ;
613
+
614
+ use super :: * ;
615
+
616
+ pub ( super ) fn any_nonnull < T : ?Sized + KnownLayout > ( ) -> NonNull < T > {
617
+ let ( size, meta) = match T :: LAYOUT . size_info {
618
+ SizeInfo :: Sized { size } => ( size, T :: PointerMetadata :: from_elem_count ( 0 ) ) ,
619
+ SizeInfo :: SliceDst ( TrailingSliceLayout { .. } ) => {
620
+ let meta = T :: PointerMetadata :: from_elem_count ( kani:: any ( ) ) ;
621
+ let size = meta. size_for_metadata ( T :: LAYOUT ) ;
622
+ kani:: assume ( size. is_some ( ) ) ;
623
+ let size = size. unwrap ( ) ;
624
+ kani:: assume ( size <= isize:: MAX as usize ) ;
625
+
626
+ ( size, meta)
627
+ }
628
+ } ;
629
+
630
+ let layout = alloc:: alloc:: Layout :: from_size_align ( size, T :: LAYOUT . align . get ( ) ) . unwrap ( ) ;
631
+
632
+ let ptr = if layout. size ( ) == 0 {
633
+ NonNull :: dangling ( )
634
+ } else {
635
+ let ptr = unsafe { alloc:: alloc:: alloc ( layout) } ;
636
+ NonNull :: new ( ptr) . unwrap_or_else ( || alloc:: alloc:: handle_alloc_error ( layout) )
637
+ } ;
638
+
639
+ T :: raw_from_ptr_len ( ptr, meta)
640
+ }
641
+
642
+ impl < T > kani:: Arbitrary for PtrInner < ' static , T >
643
+ where
644
+ T : ?Sized + KnownLayout ,
645
+ {
646
+ fn any ( ) -> Self {
647
+ unsafe { PtrInner :: new ( any_nonnull ( ) ) }
648
+ }
649
+ }
650
+
651
+ impl < ' a , T > PtrInner < ' a , T >
652
+ where
653
+ T : ?Sized + KnownLayout ,
654
+ {
655
+ fn assert_invariants ( & self ) {
656
+ let ptr = self . as_non_null ( ) ;
657
+ let size = T :: size_of_val_raw ( ptr) . unwrap ( ) ;
658
+ assert ! ( size <= isize :: MAX as usize ) ;
659
+
660
+ let addr = ptr. addr ( ) . get ( ) ;
661
+ // Assert that the referent doesn't wrap around the address space.
662
+ assert ! ( addr. checked_add( size) . is_some( ) ) ;
663
+ }
664
+ }
665
+
666
+ macro_rules! prove_foreach {
667
+ ( $generic: ident { $( $concrete: ident = $ty: ty, ) * } ) => {
668
+ $(
669
+ #[ kani:: proof]
670
+ fn $concrete( ) {
671
+ $generic:: <$ty>( ) ;
672
+ }
673
+ ) *
674
+ } ;
675
+ }
676
+
677
+ fn prove_arbitrary < T : ' static + ?Sized + KnownLayout > ( ) {
678
+ let ptr: PtrInner < ' static , T > = kani:: any ( ) ;
679
+ ptr. assert_invariants ( ) ;
680
+ }
681
+
682
+ // On 64-bit targets, Rust uses 2^61 - 1 rather than 2^63 - 1 as the maximum
683
+ // object size [1].
684
+ //
685
+ // [1] https://github.com/rust-lang/rust/blob/493c38ba371929579fe136df26eccd9516347c7a/compiler/rustc_abi/src/lib.rs#L410
686
+ pub ( super ) const MAX_SIZE : usize = 1 << 61 - 1 ;
687
+
688
+ prove_foreach ! {
689
+ prove_arbitrary {
690
+ prove_arbitrary_empty_tuple = ( ) ,
691
+ prove_arbitrary_u8 = u8 ,
692
+ prove_arbitrary_u16 = u16 ,
693
+ prove_arbitrary_slice_u8 = [ u8 ] ,
694
+ prove_arbitrary_slice_u16 = [ u16 ] ,
695
+ prove_arbitrary_large_u8 = [ u8 ; MAX_SIZE ] ,
696
+ prove_arbitrary_large_slice_u8 = [ [ u8 ; MAX_SIZE ] ] ,
697
+ prove_arbitrary_large_u16 = [ u16 ; ( MAX_SIZE ) /2 ] ,
698
+ prove_arbitrary_large_slice_u16 = [ [ u16 ; ( MAX_SIZE ) /2 ] ] ,
699
+ }
700
+ }
701
+
702
+ fn prove_slice_unchecked < T : ' static > ( ) {
703
+ let ptr: PtrInner < ' static , [ T ] > = kani:: any ( ) ;
704
+ let start = kani:: any ( ) ;
705
+ let end = kani:: any ( ) ;
706
+
707
+ kani:: assume ( start <= end) ;
708
+ kani:: assume ( end <= ptr. len ( ) ) ;
709
+
710
+ let slc = unsafe { ptr. slice_unchecked ( Range { start, end } ) } ;
711
+ slc. assert_invariants ( ) ;
712
+
713
+ assert_eq ! ( slc. len( ) , end - start) ;
714
+
715
+ let begin = unsafe { slc. as_non_null ( ) . cast :: < T > ( ) . sub ( start) } ;
716
+ assert_eq ! ( begin, ptr. as_non_null( ) . cast:: <T >( ) ) ;
717
+ }
718
+
719
+ prove_foreach ! {
720
+ prove_slice_unchecked {
721
+ prove_slice_unchecked_empty_tuple = ( ) ,
722
+ prove_slice_unchecked_u8 = u8 ,
723
+ prove_slice_unchecked_u16 = u16 ,
724
+ prove_slice_unchecked_large_u8 = [ u8 ; MAX_SIZE ] ,
725
+ prove_slice_unchecked_large_u16 = [ u16 ; ( MAX_SIZE ) /2 ] ,
726
+ }
727
+ }
728
+
729
+ fn prove_split_at < T : ' static > ( ) {
730
+ let ptr: PtrInner < ' static , [ T ] > = kani:: any ( ) ;
731
+ let l_len = kani:: any ( ) ;
732
+
733
+ kani:: assume ( l_len <= ptr. len ( ) ) ;
734
+
735
+ let ( left, right) = unsafe { ptr. split_at ( l_len) } ;
736
+ left. assert_invariants ( ) ;
737
+ right. assert_invariants ( ) ;
738
+
739
+ assert_eq ! ( left. len( ) , l_len) ;
740
+ assert_eq ! ( left. len( ) + right. len( ) , ptr. len( ) ) ;
741
+ assert_eq ! ( left. as_non_null( ) . cast:: <T >( ) , ptr. as_non_null( ) . cast:: <T >( ) ) ;
742
+ }
743
+
744
+ prove_foreach ! {
745
+ prove_split_at {
746
+ prove_split_at_empty_tuple = ( ) ,
747
+ prove_split_at_u8 = u8 ,
748
+ prove_split_at_u16 = u16 ,
749
+ prove_split_at_large_u8 = [ u8 ; MAX_SIZE ] ,
750
+ prove_split_at_large_u16 = [ u16 ; ( MAX_SIZE ) /2 ] ,
751
+ }
752
+ }
753
+
754
+ #[ kani:: proof]
755
+ fn prove_as_slice ( ) {
756
+ fn inner < T : ' static , const N : usize > ( ) {
757
+ let ptr: PtrInner < ' static , [ T ; N ] > = kani:: any ( ) ;
758
+ let slc = ptr. as_slice ( ) ;
759
+ slc. assert_invariants ( ) ;
760
+
761
+ assert_eq ! ( ptr. as_non_null( ) . cast:: <T >( ) , slc. as_non_null( ) . cast:: <T >( ) ) ;
762
+ assert_eq ! ( slc. len( ) , N ) ;
763
+ }
764
+
765
+ inner :: < ( ) , 0 > ( ) ;
766
+ inner :: < ( ) , 1 > ( ) ;
767
+ inner :: < ( ) , MAX_SIZE > ( ) ;
768
+
769
+ inner :: < u8 , 0 > ( ) ;
770
+ inner :: < u8 , 1 > ( ) ;
771
+ inner :: < u8 , MAX_SIZE > ( ) ;
772
+
773
+ inner :: < [ u8 ; MAX_SIZE / 2 ] , 0 > ( ) ;
774
+ inner :: < [ u8 ; MAX_SIZE / 2 ] , 1 > ( ) ;
775
+ inner :: < [ u8 ; MAX_SIZE / 2 ] , 2 > ( ) ;
776
+
777
+ inner :: < [ u8 ; MAX_SIZE ] , 0 > ( ) ;
778
+ inner :: < [ u8 ; MAX_SIZE ] , 1 > ( ) ;
779
+ }
780
+
781
+ impl kani:: Arbitrary for CastType {
782
+ fn any ( ) -> CastType {
783
+ if kani:: any ( ) {
784
+ CastType :: Prefix
785
+ } else {
786
+ CastType :: Suffix
787
+ }
788
+ }
789
+ }
790
+
791
+ fn prove_try_cast_into < T : ' static + ?Sized + KnownLayout > ( ) {
792
+ let ptr: PtrInner < ' static , [ u8 ] > = kani:: any ( ) ;
793
+ let cast_type = kani:: any ( ) ;
794
+ let meta = kani:: any ( ) ;
795
+
796
+ match ptr. try_cast_into :: < T > ( cast_type, meta) {
797
+ Ok ( ( t, rest) ) => {
798
+ t. assert_invariants ( ) ;
799
+
800
+ let ptr_u8 = ptr. as_non_null ( ) . cast :: < u8 > ( ) ;
801
+ let t_u8 = t. as_non_null ( ) . cast :: < u8 > ( ) ;
802
+ let rest_u8 = rest. as_non_null ( ) . cast :: < u8 > ( ) ;
803
+ match cast_type {
804
+ CastType :: Prefix => {
805
+ assert_eq ! ( t_u8, ptr_u8) ;
806
+ // TODO: Assert that `t` and `rest` are contiguous and
807
+ // non-overlapping.
808
+ }
809
+ CastType :: Suffix => {
810
+ assert_eq ! ( rest_u8, ptr_u8) ;
811
+ // Asserts that `rest` and `t` are contiguous and
812
+ // non-overlapping.
813
+ assert_eq ! ( unsafe { rest_u8. add( rest. len( ) ) } , t_u8) ;
814
+ }
815
+ }
816
+ }
817
+ Err ( CastError :: Alignment ( _) ) => {
818
+ // TODO: Prove that there would have been an alignment problem.
819
+ }
820
+ Err ( CastError :: Size ( _) ) => {
821
+ // TODO: Prove that there would have been a size problem.
822
+ }
823
+ }
824
+ }
825
+
826
+ prove_foreach ! {
827
+ prove_try_cast_into {
828
+ prove_try_cast_into_empty_tuple = ( ) ,
829
+ prove_try_cast_into_u8 = u8 ,
830
+ prove_try_cast_into_u16 = u16 ,
831
+ prove_try_cast_into_slice_u8 = [ u8 ] ,
832
+ prove_try_cast_into_slice_u16 = [ u16 ] ,
833
+ prove_try_cast_into_large_u8 = [ u8 ; MAX_SIZE ] ,
834
+ prove_try_cast_into_large_slice_u8 = [ [ u8 ; MAX_SIZE ] ] ,
835
+ prove_try_cast_into_large_u16 = [ u16 ; ( MAX_SIZE ) /2 ] ,
836
+ prove_try_cast_into_large_slice_u16 = [ [ u16 ; ( MAX_SIZE ) /2 ] ] ,
837
+ }
838
+ }
839
+ }
0 commit comments