6464) ]
6565#![ allow( missing_docs) ]
6666
67- use safety:: requires;
6867use crate :: marker:: { DiscriminantKind , Tuple } ;
6968use crate :: mem:: SizedTypeProperties ;
7069use crate :: { ptr, ub_checks} ;
70+ use safety:: { ensures, requires} ;
7171
7272#[ cfg( kani) ]
7373use crate :: kani;
@@ -3663,7 +3663,8 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
36633663#[ requires( ub_checks:: can_dereference( x) && ub_checks:: can_write( x) ) ]
36643664#[ requires( ub_checks:: can_dereference( y) && ub_checks:: can_write( y) ) ]
36653665#[ 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) ) ]
36673668pub const unsafe fn typed_swap < T > ( x : * mut T , y : * mut T ) {
36683669 // SAFETY: The caller provided single non-overlapping items behind
36693670 // 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)
37373738#[ unstable( feature = "core_intrinsics" , issue = "none" ) ]
37383739#[ rustc_intrinsic]
37393740#[ 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 ] ) ) ]
37403744pub unsafe fn vtable_size ( _ptr : * const ( ) ) -> usize {
37413745 unreachable ! ( )
37423746}
@@ -3750,6 +3754,9 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
37503754#[ unstable( feature = "core_intrinsics" , issue = "none" ) ]
37513755#[ rustc_intrinsic]
37523756#[ 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 ] ) ) ]
37533760pub unsafe fn vtable_align ( _ptr : * const ( ) ) -> usize {
37543761 unreachable ! ( )
37553762}
@@ -4034,6 +4041,13 @@ pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *cons
40344041#[ inline( always) ]
40354042#[ cfg_attr( miri, track_caller) ] // even without panics, this helps for Miri backtraces
40364043#[ 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) } ) ]
40374051pub const unsafe fn copy_nonoverlapping < T > ( src : * const T , dst : * mut T , count : usize ) {
40384052 #[ cfg_attr( bootstrap, rustc_const_stable( feature = "const_intrinsic_copy" , since = "1.83.0" ) ) ]
40394053 #[ 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
41414155#[ inline( always) ]
41424156#[ cfg_attr( miri, track_caller) ] // even without panics, this helps for Miri backtraces
41434157#[ 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) ) ) ]
41444163pub const unsafe fn copy < T > ( src : * const T , dst : * mut T , count : usize ) {
41454164 #[ cfg_attr( bootstrap, rustc_const_stable( feature = "const_intrinsic_copy" , since = "1.83.0" ) ) ]
41464165 #[ 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) {
42254244#[ inline( always) ]
42264245#[ cfg_attr( miri, track_caller) ] // even without panics, this helps for Miri backtraces
42274246#[ 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) ) ) ]
42284253pub const unsafe fn write_bytes < T > ( dst : * mut T , val : u8 , count : usize ) {
42294254 #[ cfg_attr( bootstrap, rustc_const_stable( feature = "const_ptr_write" , since = "1.83.0" ) ) ]
42304255 #[ cfg_attr( not( bootstrap) , rustc_intrinsic_const_stable_indirect) ]
@@ -4513,6 +4538,36 @@ pub const unsafe fn copysignf128(_x: f128, _y: f128) -> f128 {
45134538 unimplemented ! ( ) ;
45144539}
45154540
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+
45164571/// Inform Miri that a given pointer definitely has a certain alignment.
45174572#[ cfg( miri) ]
45184573#[ rustc_allow_const_fn_unstable( const_eval_select) ]
@@ -4538,35 +4593,99 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
45384593}
45394594
45404595#[ cfg( kani) ]
4541- #[ unstable( feature= "kani" , issue= "none" ) ]
4596+ #[ unstable( feature = "kani" , issue = "none" ) ]
45424597mod verify {
4543- use core:: { cmp, fmt} ;
45444598 use super :: * ;
45454599 use crate :: kani;
4600+ use core:: mem:: MaybeUninit ;
4601+ use kani:: { AllocationStatus , Arbitrary , ArbitraryPointer , PointerGenerator } ;
45464602
45474603 #[ kani:: proof_for_contract( typed_swap) ]
45484604 pub fn check_typed_swap_u8 ( ) {
4549- check_swap :: < u8 > ( )
4605+ run_with_arbitrary_ptrs :: < u8 > ( |x , y| unsafe { typed_swap ( x , y ) } ) ;
45504606 }
45514607
45524608 #[ kani:: proof_for_contract( typed_swap) ]
45534609 pub fn check_typed_swap_char ( ) {
4554- check_swap :: < char > ( )
4610+ run_with_arbitrary_ptrs :: < char > ( |x , y| unsafe { typed_swap ( x , y ) } ) ;
45554611 }
45564612
45574613 #[ kani:: proof_for_contract( typed_swap) ]
45584614 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 ) } ) ;
45604616 }
45614617
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+ }
45674622
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
45714690 }
45724691}
0 commit comments