@@ -519,3 +519,234 @@ mod tests {
519
519
}
520
520
}
521
521
}
522
+
523
+ #[ cfg( kani) ]
524
+ mod proofs {
525
+ use core:: alloc:: Layout ;
526
+
527
+ use crate :: { SizeInfo , TrailingSliceLayout } ;
528
+
529
+ use super :: * ;
530
+
531
+ impl < T > kani:: Arbitrary for PtrInner < ' static , T >
532
+ where
533
+ T : ?Sized + KnownLayout ,
534
+ {
535
+ fn any ( ) -> Self {
536
+ let ( size, meta) = match T :: LAYOUT . size_info {
537
+ SizeInfo :: Sized { size } => ( size, T :: PointerMetadata :: from_elem_count ( 0 ) ) ,
538
+ SizeInfo :: SliceDst ( TrailingSliceLayout { offset, elem_size } ) => {
539
+ let meta = T :: PointerMetadata :: from_elem_count ( kani:: any ( ) ) ;
540
+ let size = meta. size_for_metadata ( T :: LAYOUT ) ;
541
+ kani:: assume ( size. is_some ( ) ) ;
542
+ let size = size. unwrap ( ) ;
543
+ kani:: assume ( size <= isize:: MAX as usize ) ;
544
+
545
+ ( size, meta)
546
+ }
547
+ } ;
548
+
549
+ let layout =
550
+ alloc:: alloc:: Layout :: from_size_align ( size, T :: LAYOUT . align . get ( ) ) . unwrap ( ) ;
551
+
552
+ let ptr = if layout. size ( ) == 0 {
553
+ NonNull :: dangling ( )
554
+ } else {
555
+ let ptr = unsafe { alloc:: alloc:: alloc ( layout) } ;
556
+ NonNull :: new ( ptr) . unwrap_or_else ( || alloc:: alloc:: handle_alloc_error ( layout) )
557
+ } ;
558
+
559
+ let ptr = T :: raw_from_ptr_len ( ptr, meta) ;
560
+ unsafe { PtrInner :: new ( ptr) }
561
+ }
562
+ }
563
+
564
+ impl < ' a , T > PtrInner < ' a , T >
565
+ where
566
+ T : ?Sized + KnownLayout ,
567
+ {
568
+ fn assert_invariants ( & self ) {
569
+ let ptr = self . as_non_null ( ) ;
570
+ let size = T :: size_of_val_raw ( ptr) . unwrap ( ) ;
571
+ assert ! ( size <= isize :: MAX as usize ) ;
572
+
573
+ let addr = ptr. addr ( ) . get ( ) ;
574
+ // Assert that the referent doesn't wrap around the address space.
575
+ assert ! ( addr. checked_add( size) . is_some( ) ) ;
576
+ }
577
+ }
578
+
579
+ macro_rules! prove_foreach {
580
+ ( $generic: ident { $( $concrete: ident = $ty: ty, ) * } ) => {
581
+ $(
582
+ #[ kani:: proof]
583
+ fn $concrete( ) {
584
+ $generic:: <$ty>( ) ;
585
+ }
586
+ ) *
587
+ } ;
588
+ }
589
+
590
+ fn prove_arbitrary < T : ' static + ?Sized + KnownLayout > ( ) {
591
+ let ptr: PtrInner < ' static , T > = kani:: any ( ) ;
592
+ ptr. assert_invariants ( ) ;
593
+ }
594
+
595
+ // On 64-bit targets, Rust uses 2^61 - 1 rather than 2^63 - 1 as the maximum
596
+ // object size [1].
597
+ //
598
+ // [1] https://github.com/rust-lang/rust/blob/493c38ba371929579fe136df26eccd9516347c7a/compiler/rustc_abi/src/lib.rs#L410
599
+ const MAX_SIZE : usize = 1 << 61 - 1 ;
600
+
601
+ prove_foreach ! {
602
+ prove_arbitrary {
603
+ prove_arbitrary_empty_tuple = ( ) ,
604
+ prove_arbitrary_u8 = u8 ,
605
+ prove_arbitrary_u16 = u16 ,
606
+ prove_arbitrary_slice_u8 = [ u8 ] ,
607
+ prove_arbitrary_slice_u16 = [ u16 ] ,
608
+ prove_arbitrary_large_u8 = [ u8 ; MAX_SIZE ] ,
609
+ prove_arbitrary_large_slice_u8 = [ [ u8 ; MAX_SIZE ] ] ,
610
+ prove_arbitrary_large_u16 = [ u16 ; ( MAX_SIZE ) /2 ] ,
611
+ prove_arbitrary_large_slice_u16 = [ [ u16 ; ( MAX_SIZE ) /2 ] ] ,
612
+ }
613
+ }
614
+
615
+ fn prove_slice_unchecked < T : ' static > ( ) {
616
+ let ptr: PtrInner < ' static , [ T ] > = kani:: any ( ) ;
617
+ let start = kani:: any ( ) ;
618
+ let end = kani:: any ( ) ;
619
+
620
+ kani:: assume ( start <= end) ;
621
+ kani:: assume ( end <= ptr. len ( ) ) ;
622
+
623
+ let slc = unsafe { ptr. slice_unchecked ( Range { start, end } ) } ;
624
+ slc. assert_invariants ( ) ;
625
+
626
+ assert_eq ! ( slc. len( ) , end - start) ;
627
+
628
+ let begin = unsafe { slc. as_non_null ( ) . cast :: < T > ( ) . sub ( start) } ;
629
+ assert_eq ! ( begin, ptr. as_non_null( ) . cast:: <T >( ) ) ;
630
+ }
631
+
632
+ prove_foreach ! {
633
+ prove_slice_unchecked {
634
+ prove_slice_unchecked_empty_tuple = ( ) ,
635
+ prove_slice_unchecked_u8 = u8 ,
636
+ prove_slice_unchecked_u16 = u16 ,
637
+ prove_slice_unchecked_large_u8 = [ u8 ; MAX_SIZE ] ,
638
+ prove_slice_unchecked_large_u16 = [ u16 ; ( MAX_SIZE ) /2 ] ,
639
+ }
640
+ }
641
+
642
+ fn prove_split_at < T : ' static > ( ) {
643
+ let ptr: PtrInner < ' static , [ T ] > = kani:: any ( ) ;
644
+ let l_len = kani:: any ( ) ;
645
+
646
+ kani:: assume ( l_len <= ptr. len ( ) ) ;
647
+
648
+ let ( left, right) = unsafe { ptr. split_at ( l_len) } ;
649
+ left. assert_invariants ( ) ;
650
+ right. assert_invariants ( ) ;
651
+
652
+ assert_eq ! ( left. len( ) , l_len) ;
653
+ assert_eq ! ( left. len( ) + right. len( ) , ptr. len( ) ) ;
654
+ assert_eq ! ( left. as_non_null( ) . cast:: <T >( ) , ptr. as_non_null( ) . cast:: <T >( ) ) ;
655
+ }
656
+
657
+ prove_foreach ! {
658
+ prove_split_at {
659
+ prove_split_at_empty_tuple = ( ) ,
660
+ prove_split_at_u8 = u8 ,
661
+ prove_split_at_u16 = u16 ,
662
+ prove_split_at_large_u8 = [ u8 ; MAX_SIZE ] ,
663
+ prove_split_at_large_u16 = [ u16 ; ( MAX_SIZE ) /2 ] ,
664
+ }
665
+ }
666
+
667
+ #[ kani:: proof]
668
+ fn prove_as_slice ( ) {
669
+ fn inner < T : ' static , const N : usize > ( ) {
670
+ let ptr: PtrInner < ' static , [ T ; N ] > = kani:: any ( ) ;
671
+ let slc = ptr. as_slice ( ) ;
672
+ slc. assert_invariants ( ) ;
673
+
674
+ assert_eq ! ( ptr. as_non_null( ) . cast:: <T >( ) , slc. as_non_null( ) . cast:: <T >( ) ) ;
675
+ assert_eq ! ( slc. len( ) , N ) ;
676
+ }
677
+
678
+ inner :: < ( ) , 0 > ( ) ;
679
+ inner :: < ( ) , 1 > ( ) ;
680
+ inner :: < ( ) , MAX_SIZE > ( ) ;
681
+
682
+ inner :: < u8 , 0 > ( ) ;
683
+ inner :: < u8 , 1 > ( ) ;
684
+ inner :: < u8 , MAX_SIZE > ( ) ;
685
+
686
+ inner :: < [ u8 ; MAX_SIZE / 2 ] , 0 > ( ) ;
687
+ inner :: < [ u8 ; MAX_SIZE / 2 ] , 1 > ( ) ;
688
+ inner :: < [ u8 ; MAX_SIZE / 2 ] , 2 > ( ) ;
689
+
690
+ inner :: < [ u8 ; MAX_SIZE ] , 0 > ( ) ;
691
+ inner :: < [ u8 ; MAX_SIZE ] , 1 > ( ) ;
692
+ }
693
+
694
+ impl kani:: Arbitrary for CastType {
695
+ fn any ( ) -> CastType {
696
+ if kani:: any ( ) {
697
+ CastType :: Prefix
698
+ } else {
699
+ CastType :: Suffix
700
+ }
701
+ }
702
+ }
703
+
704
+ fn prove_try_cast_into < T : ' static + ?Sized + KnownLayout > ( ) {
705
+ let ptr: PtrInner < ' static , [ u8 ] > = kani:: any ( ) ;
706
+ let cast_type = kani:: any ( ) ;
707
+ let meta = kani:: any ( ) ;
708
+
709
+ match ptr. try_cast_into :: < T > ( cast_type, meta) {
710
+ Ok ( ( t, rest) ) => {
711
+ t. assert_invariants ( ) ;
712
+
713
+ let ptr_u8 = ptr. as_non_null ( ) . cast :: < u8 > ( ) ;
714
+ let t_u8 = t. as_non_null ( ) . cast :: < u8 > ( ) ;
715
+ let rest_u8 = rest. as_non_null ( ) . cast :: < u8 > ( ) ;
716
+ match cast_type {
717
+ CastType :: Prefix => {
718
+ assert_eq ! ( t_u8, ptr_u8) ;
719
+ // TODO: Assert that `t` and `rest` are contiguous and
720
+ // non-overlapping.
721
+ }
722
+ CastType :: Suffix => {
723
+ assert_eq ! ( rest_u8, ptr_u8) ;
724
+ // Asserts that `rest` and `t` are contiguous and
725
+ // non-overlapping.
726
+ assert_eq ! ( unsafe { rest_u8. add( rest. len( ) ) } , t_u8) ;
727
+ }
728
+ }
729
+ }
730
+ Err ( CastError :: Alignment ( _) ) => {
731
+ // TODO: Prove that there would have been an alignment problem.
732
+ }
733
+ Err ( CastError :: Size ( _) ) => {
734
+ // TODO: Prove that there would have been a size problem.
735
+ }
736
+ }
737
+ }
738
+
739
+ prove_foreach ! {
740
+ prove_try_cast_into {
741
+ prove_try_cast_into_empty_tuple = ( ) ,
742
+ prove_try_cast_into_u8 = u8 ,
743
+ prove_try_cast_into_u16 = u16 ,
744
+ prove_try_cast_into_slice_u8 = [ u8 ] ,
745
+ prove_try_cast_into_slice_u16 = [ u16 ] ,
746
+ prove_try_cast_into_large_u8 = [ u8 ; MAX_SIZE ] ,
747
+ prove_try_cast_into_large_slice_u8 = [ [ u8 ; MAX_SIZE ] ] ,
748
+ prove_try_cast_into_large_u16 = [ u16 ; ( MAX_SIZE ) /2 ] ,
749
+ prove_try_cast_into_large_slice_u16 = [ [ u16 ; ( MAX_SIZE ) /2 ] ] ,
750
+ }
751
+ }
752
+ }
0 commit comments