64
64
) ]
65
65
#![ allow( missing_docs) ]
66
66
67
- use safety:: requires;
68
67
use crate :: marker:: { DiscriminantKind , Tuple } ;
69
68
use crate :: mem:: SizedTypeProperties ;
70
69
use crate :: { ptr, ub_checks} ;
70
+ use safety:: { ensures, requires} ;
71
71
72
72
#[ cfg( kani) ]
73
73
use crate :: kani;
@@ -3663,7 +3663,8 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
3663
3663
#[ requires( ub_checks:: can_dereference( x) && ub_checks:: can_write( x) ) ]
3664
3664
#[ requires( ub_checks:: can_dereference( y) && ub_checks:: can_write( y) ) ]
3665
3665
#[ requires( x. addr( ) != y. addr( ) || core:: mem:: size_of:: <T >( ) == 0 ) ]
3666
- #[ requires( ( x. addr( ) >= y. addr( ) + core:: mem:: size_of:: <T >( ) ) || ( y. addr( ) >= x. addr( ) + core:: mem:: size_of:: <T >( ) ) ) ]
3666
+ #[ requires( ub_checks:: maybe_is_nonoverlapping( x as * const ( ) , y as * const ( ) , size_of:: <T >( ) , 1 ) ) ]
3667
+ #[ ensures( |_| ub_checks:: can_dereference( x) && ub_checks:: can_dereference( y) ) ]
3667
3668
pub const unsafe fn typed_swap < T > ( x : * mut T , y : * mut T ) {
3668
3669
// SAFETY: The caller provided single non-overlapping items behind
3669
3670
// pointers, so swapping them with `count: 1` is fine.
@@ -3737,6 +3738,9 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
3737
3738
#[ unstable( feature = "core_intrinsics" , issue = "none" ) ]
3738
3739
#[ rustc_intrinsic]
3739
3740
#[ rustc_intrinsic_must_be_overridden]
3741
+ // VTable pointers must be valid for dereferencing at least 3 `usize` (size, alignment and drop):
3742
+ // <https://github.com/rust-lang/unsafe-code-guidelines/issues/166>
3743
+ #[ requires( ub_checks:: can_dereference( _ptr as * const [ usize ; 3 ] ) ) ]
3740
3744
pub unsafe fn vtable_size ( _ptr : * const ( ) ) -> usize {
3741
3745
unreachable ! ( )
3742
3746
}
@@ -3750,6 +3754,9 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
3750
3754
#[ unstable( feature = "core_intrinsics" , issue = "none" ) ]
3751
3755
#[ rustc_intrinsic]
3752
3756
#[ rustc_intrinsic_must_be_overridden]
3757
+ // VTable pointers must be valid for dereferencing at least 3 `usize` (size, alignment and drop):
3758
+ // <https://github.com/rust-lang/unsafe-code-guidelines/issues/166>
3759
+ #[ requires( ub_checks:: can_dereference( _ptr as * const [ usize ; 3 ] ) ) ]
3753
3760
pub unsafe fn vtable_align ( _ptr : * const ( ) ) -> usize {
3754
3761
unreachable ! ( )
3755
3762
}
@@ -4034,6 +4041,13 @@ pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *cons
4034
4041
#[ inline( always) ]
4035
4042
#[ cfg_attr( miri, track_caller) ] // even without panics, this helps for Miri backtraces
4036
4043
#[ rustc_diagnostic_item = "ptr_copy_nonoverlapping" ]
4044
+ // Copy is "untyped".
4045
+ #[ cfg_attr( kani, kani:: modifies( crate :: ptr:: slice_from_raw_parts( dst, count) ) ) ]
4046
+ #[ requires( !count. overflowing_mul( size_of:: <T >( ) ) . 1
4047
+ && ub_checks:: can_dereference( core:: ptr:: slice_from_raw_parts( src as * const crate :: mem:: MaybeUninit <T >, count) )
4048
+ && ub_checks:: can_write( core:: ptr:: slice_from_raw_parts_mut( dst, count) )
4049
+ && ub_checks:: maybe_is_nonoverlapping( src as * const ( ) , dst as * const ( ) , size_of:: <T >( ) , count) ) ]
4050
+ #[ ensures( |_| { check_copy_untyped( src, dst, count) } ) ]
4037
4051
pub const unsafe fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) {
4038
4052
#[ cfg_attr( bootstrap, rustc_const_stable( feature = "const_intrinsic_copy" , since = "1.83.0" ) ) ]
4039
4053
#[ cfg_attr( not( bootstrap) , rustc_intrinsic_const_stable_indirect) ]
@@ -4141,6 +4155,11 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
4141
4155
#[ inline( always) ]
4142
4156
#[ cfg_attr( miri, track_caller) ] // even without panics, this helps for Miri backtraces
4143
4157
#[ rustc_diagnostic_item = "ptr_copy" ]
4158
+ #[ requires( !count. overflowing_mul( size_of:: <T >( ) ) . 1
4159
+ && ub_checks:: can_dereference( core:: ptr:: slice_from_raw_parts( src as * const crate :: mem:: MaybeUninit <T >, count) )
4160
+ && ub_checks:: can_write( core:: ptr:: slice_from_raw_parts_mut( dst, count) ) ) ]
4161
+ #[ ensures( |_| { check_copy_untyped( src, dst, count) } ) ]
4162
+ #[ cfg_attr( kani, kani:: modifies( crate :: ptr:: slice_from_raw_parts( dst, count) ) ) ]
4144
4163
pub const unsafe fn copy < T > ( src : * const T , dst : * mut T , count : usize ) {
4145
4164
#[ cfg_attr( bootstrap, rustc_const_stable( feature = "const_intrinsic_copy" , since = "1.83.0" ) ) ]
4146
4165
#[ cfg_attr( not( bootstrap) , rustc_intrinsic_const_stable_indirect) ]
@@ -4225,6 +4244,12 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
4225
4244
#[ inline( always) ]
4226
4245
#[ cfg_attr( miri, track_caller) ] // even without panics, this helps for Miri backtraces
4227
4246
#[ rustc_diagnostic_item = "ptr_write_bytes" ]
4247
+ #[ requires( !count. overflowing_mul( size_of:: <T >( ) ) . 1
4248
+ && ub_checks:: can_write( core:: ptr:: slice_from_raw_parts_mut( dst, count) ) ) ]
4249
+ #[ requires( ub_checks:: maybe_is_aligned_and_not_null( dst as * const ( ) , align_of:: <T >( ) , T :: IS_ZST || count == 0 ) ) ]
4250
+ #[ ensures( |_|
4251
+ ub_checks:: can_dereference( crate :: ptr:: slice_from_raw_parts( dst as * const u8 , count * size_of:: <T >( ) ) ) ) ]
4252
+ #[ cfg_attr( kani, kani:: modifies( crate :: ptr:: slice_from_raw_parts( dst, count) ) ) ]
4228
4253
pub const unsafe fn write_bytes < T > ( dst : * mut T , val : u8 , count : usize ) {
4229
4254
#[ cfg_attr( bootstrap, rustc_const_stable( feature = "const_ptr_write" , since = "1.83.0" ) ) ]
4230
4255
#[ cfg_attr( not( bootstrap) , rustc_intrinsic_const_stable_indirect) ]
@@ -4513,6 +4538,36 @@ pub const unsafe fn copysignf128(_x: f128, _y: f128) -> f128 {
4513
4538
unimplemented ! ( ) ;
4514
4539
}
4515
4540
4541
+ /// Return whether the initialization state is preserved.
4542
+ ///
4543
+ /// For untyped copy, done via `copy` and `copy_nonoverlapping`, the copies of non-initialized
4544
+ /// bytes (such as padding bytes) should result in a non-initialized copy, while copies of
4545
+ /// initialized bytes result in initialized bytes.
4546
+ ///
4547
+ /// It is UB to read the uninitialized bytes, so we cannot compare their values only their
4548
+ /// initialization state.
4549
+ ///
4550
+ /// This is used for contracts only.
4551
+ ///
4552
+ /// FIXME: Change this once we add support to quantifiers.
4553
+ #[ allow( dead_code) ]
4554
+ #[ allow( unused_variables) ]
4555
+ fn check_copy_untyped < T > ( src : * const T , dst : * mut T , count : usize ) -> bool {
4556
+ #[ cfg( kani) ]
4557
+ if count > 0 {
4558
+ let byte = kani:: any_where ( |sz : & usize | * sz < size_of :: < T > ( ) ) ;
4559
+ let elem = kani:: any_where ( |val : & usize | * val < count) ;
4560
+ let src_data = src as * const u8 ;
4561
+ let dst_data = unsafe { dst. add ( elem) } as * const u8 ;
4562
+ ub_checks:: can_dereference ( unsafe { src_data. add ( byte) } )
4563
+ == ub_checks:: can_dereference ( unsafe { dst_data. add ( byte) } )
4564
+ } else {
4565
+ true
4566
+ }
4567
+ #[ cfg( not( kani) ) ]
4568
+ false
4569
+ }
4570
+
4516
4571
/// Inform Miri that a given pointer definitely has a certain alignment.
4517
4572
#[ cfg( miri) ]
4518
4573
#[ rustc_allow_const_fn_unstable( const_eval_select) ]
@@ -4538,35 +4593,99 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
4538
4593
}
4539
4594
4540
4595
#[ cfg( kani) ]
4541
- #[ unstable( feature= "kani" , issue= "none" ) ]
4596
+ #[ unstable( feature = "kani" , issue = "none" ) ]
4542
4597
mod verify {
4543
- use core:: { cmp, fmt} ;
4544
4598
use super :: * ;
4545
4599
use crate :: kani;
4600
+ use core:: mem:: MaybeUninit ;
4601
+ use kani:: { AllocationStatus , Arbitrary , ArbitraryPointer , PointerGenerator } ;
4546
4602
4547
4603
#[ kani:: proof_for_contract( typed_swap) ]
4548
4604
pub fn check_typed_swap_u8 ( ) {
4549
- check_swap :: < u8 > ( )
4605
+ run_with_arbitrary_ptrs :: < u8 > ( |x , y| unsafe { typed_swap ( x , y ) } ) ;
4550
4606
}
4551
4607
4552
4608
#[ kani:: proof_for_contract( typed_swap) ]
4553
4609
pub fn check_typed_swap_char ( ) {
4554
- check_swap :: < char > ( )
4610
+ run_with_arbitrary_ptrs :: < char > ( |x , y| unsafe { typed_swap ( x , y ) } ) ;
4555
4611
}
4556
4612
4557
4613
#[ kani:: proof_for_contract( typed_swap) ]
4558
4614
pub fn check_typed_swap_non_zero ( ) {
4559
- check_swap :: < core:: num:: NonZeroI32 > ( )
4615
+ run_with_arbitrary_ptrs :: < core:: num:: NonZeroI32 > ( |x , y| unsafe { typed_swap ( x , y ) } ) ;
4560
4616
}
4561
4617
4562
- pub fn check_swap < T : kani:: Arbitrary + Copy + cmp:: PartialEq + fmt:: Debug > ( ) {
4563
- let mut x = kani:: any :: < T > ( ) ;
4564
- let old_x = x;
4565
- let mut y = kani:: any :: < T > ( ) ;
4566
- let old_y = y;
4618
+ #[ kani:: proof_for_contract( copy) ]
4619
+ fn check_copy ( ) {
4620
+ run_with_arbitrary_ptrs :: < char > ( |src, dst| unsafe { copy ( src, dst, kani:: any ( ) ) } ) ;
4621
+ }
4567
4622
4568
- unsafe { typed_swap ( & mut x, & mut y) } ;
4569
- assert_eq ! ( y, old_x) ;
4570
- assert_eq ! ( x, old_y) ;
4623
+ #[ kani:: proof_for_contract( copy_nonoverlapping) ]
4624
+ fn check_copy_nonoverlapping ( ) {
4625
+ // Note: cannot use `ArbitraryPointer` here.
4626
+ // The `ArbitraryPtr` will arbitrarily initialize memory by indirectly invoking
4627
+ // `copy_nonoverlapping`.
4628
+ // Kani contract checking would fail due to existing restriction on calls to
4629
+ // the function under verification.
4630
+ let gen_any_ptr = |buf : & mut [ MaybeUninit < char > ; 100 ] | -> * mut char {
4631
+ let base = buf. as_mut_ptr ( ) as * mut u8 ;
4632
+ base. wrapping_add ( kani:: any_where ( |offset : & usize | * offset < 400 ) ) as * mut char
4633
+ } ;
4634
+ let mut buffer1 = [ MaybeUninit :: < char > :: uninit ( ) ; 100 ] ;
4635
+ for i in 0 ..100 {
4636
+ if kani:: any ( ) {
4637
+ buffer1[ i] = MaybeUninit :: new ( kani:: any ( ) ) ;
4638
+ }
4639
+ }
4640
+ let mut buffer2 = [ MaybeUninit :: < char > :: uninit ( ) ; 100 ] ;
4641
+ let src = gen_any_ptr ( & mut buffer1) ;
4642
+ let dst = if kani:: any ( ) { gen_any_ptr ( & mut buffer2) } else { gen_any_ptr ( & mut buffer1) } ;
4643
+ unsafe { copy_nonoverlapping ( src, dst, kani:: any ( ) ) }
4644
+ }
4645
+
4646
+ // FIXME: Enable this harness once <https://github.com/model-checking/kani/issues/90> is fixed.
4647
+ // Harness triggers a spurious failure when writing 0 bytes to an invalid memory location,
4648
+ // which is a safe operation.
4649
+ #[ cfg( not( kani) ) ]
4650
+ #[ kani:: proof_for_contract( write_bytes) ]
4651
+ fn check_write_bytes ( ) {
4652
+ let mut generator = PointerGenerator :: < 100 > :: new ( ) ;
4653
+ let ArbitraryPointer {
4654
+ ptr,
4655
+ status,
4656
+ ..
4657
+ } = generator. any_alloc_status :: < char > ( ) ;
4658
+ kani:: assume ( supported_status ( status) ) ;
4659
+ unsafe { write_bytes ( ptr, kani:: any ( ) , kani:: any ( ) ) } ;
4660
+ }
4661
+
4662
+ fn run_with_arbitrary_ptrs < T : Arbitrary > ( harness : impl Fn ( * mut T , * mut T ) ) {
4663
+ let mut generator1 = PointerGenerator :: < 100 > :: new ( ) ;
4664
+ let mut generator2 = PointerGenerator :: < 100 > :: new ( ) ;
4665
+ let ArbitraryPointer {
4666
+ ptr : src,
4667
+ status : src_status,
4668
+ ..
4669
+ } = generator1. any_alloc_status :: < T > ( ) ;
4670
+ let ArbitraryPointer {
4671
+ ptr : dst,
4672
+ status : dst_status,
4673
+ ..
4674
+ } = if kani:: any ( ) {
4675
+ generator1. any_alloc_status :: < T > ( )
4676
+ } else {
4677
+ generator2. any_alloc_status :: < T > ( )
4678
+ } ;
4679
+ kani:: assume ( supported_status ( src_status) ) ;
4680
+ kani:: assume ( supported_status ( dst_status) ) ;
4681
+ harness ( src, dst) ;
4682
+ }
4683
+
4684
+ /// Return whether the current status is supported by Kani's contract.
4685
+ ///
4686
+ /// Kani memory predicates currently doesn't support pointers to dangling or dead allocations.
4687
+ /// Thus, we have to explicitly exclude those cases.
4688
+ fn supported_status ( status : AllocationStatus ) -> bool {
4689
+ status != AllocationStatus :: Dangling && status != AllocationStatus :: DeadObject
4571
4690
}
4572
4691
}
0 commit comments