@@ -8,11 +8,14 @@ use crate::hint::assert_unchecked;
88use crate :: iter:: {
99 FusedIterator , TrustedLen , TrustedRandomAccess , TrustedRandomAccessNoCoerce , UncheckedIterator ,
1010} ;
11+ #[ cfg( kani) ]
12+ use crate :: kani;
1113use crate :: marker:: PhantomData ;
1214use crate :: mem:: { self , SizedTypeProperties } ;
1315use crate :: num:: NonZero ;
1416use crate :: ptr:: { NonNull , without_provenance, without_provenance_mut} ;
1517use crate :: { cmp, fmt} ;
18+ use crate :: ub_checks:: Invariant ;
1619
1720#[ stable( feature = "boxed_slice_into_iter" , since = "1.80.0" ) ]
1821impl < T > !Iterator for [ T ] { }
@@ -157,6 +160,32 @@ impl<T> AsRef<[T]> for Iter<'_, T> {
157160 }
158161}
159162
163+ #[ unstable( feature = "ub_checks" , issue = "none" ) ]
164+ impl < T > Invariant for Iter < ' _ , T > {
165+ /// An iterator can be safely used if its pointer can be read for its current length.
166+ ///
167+ /// If the type is a ZST or the encoded length is `0`, the only safety requirement is that
168+ /// its pointer is aligned (since zero-size access is always safe for aligned pointers).
169+ ///
170+ /// For other cases, we need to ensure that it is safe to read the memory between
171+ /// `self.ptr` and `self.end_or_len`.
172+ fn is_safe ( & self ) -> bool {
173+ let ty_size = crate :: mem:: size_of :: < T > ( ) ;
174+ // Use `abs_diff` since `end_or_len` may be smaller than `ptr` if `T` is a ZST.
175+ let distance = self . ptr . addr ( ) . get ( ) . abs_diff ( self . end_or_len as usize ) ;
176+ if ty_size == 0 || distance == 0 {
177+ self . ptr . is_aligned ( )
178+ } else {
179+ let slice_ptr: * const [ T ] =
180+ crate :: ptr:: from_raw_parts ( self . ptr . as_ptr ( ) , distance / ty_size) ;
181+ crate :: ub_checks:: same_allocation ( self . ptr . as_ptr ( ) , self . end_or_len )
182+ && self . ptr . addr ( ) . get ( ) <= self . end_or_len as usize
183+ && distance % ty_size == 0
184+ && crate :: ub_checks:: can_dereference ( slice_ptr)
185+ }
186+ }
187+ }
188+
160189/// Mutable slice iterator.
161190///
162191/// This struct is created by the [`iter_mut`] method on [slices].
@@ -196,6 +225,29 @@ pub struct IterMut<'a, T: 'a> {
196225 _marker : PhantomData < & ' a mut T > ,
197226}
198227
228+ #[ unstable( feature = "ub_checks" , issue = "none" ) ]
229+ impl < T > Invariant for IterMut < ' _ , T > {
230+ /// This is similar to [Iter] with an extra write requirement.
231+ ///
232+ /// It must be safe to write in the memory interval between `self.ptr`
233+ /// and `self.end_or_len`.
234+ fn is_safe ( & self ) -> bool {
235+ let ty_size = crate :: mem:: size_of :: < T > ( ) ;
236+ let distance = self . ptr . addr ( ) . get ( ) . abs_diff ( self . end_or_len as usize ) ;
237+ if ty_size == 0 || distance == 0 {
238+ self . ptr . is_aligned ( )
239+ } else {
240+ let slice_ptr: * mut [ T ] =
241+ crate :: ptr:: from_raw_parts_mut ( self . ptr . as_ptr ( ) , distance / ty_size) ;
242+ crate :: ub_checks:: same_allocation ( self . ptr . as_ptr ( ) , self . end_or_len )
243+ && self . ptr . addr ( ) . get ( ) <= self . end_or_len as usize
244+ && distance % ty_size == 0
245+ && crate :: ub_checks:: can_dereference ( slice_ptr)
246+ && crate :: ub_checks:: can_write ( slice_ptr)
247+ }
248+ }
249+ }
250+
199251#[ stable( feature = "core_impl_debug" , since = "1.9.0" ) ]
200252impl < T : fmt:: Debug > fmt:: Debug for IterMut < ' _ , T > {
201253 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
@@ -3464,3 +3516,137 @@ impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for ChunkByMut<'a, T, P> {
34643516 f. debug_struct ( "ChunkByMut" ) . field ( "slice" , & self . slice ) . finish ( )
34653517 }
34663518}
3519+
3520+ /// Verify the safety of the code implemented in this module (including generated code from macros).
3521+ #[ cfg( kani) ]
3522+ #[ unstable( feature = "kani" , issue = "none" ) ]
3523+ mod verify {
3524+ use super :: * ;
3525+ use crate :: kani;
3526+
3527+ fn any_slice < T > ( orig_slice : & [ T ] ) -> & [ T ] {
3528+ if kani:: any ( ) {
3529+ let last = kani:: any_where ( |idx : & usize | * idx <= orig_slice. len ( ) ) ;
3530+ let first = kani:: any_where ( |idx : & usize | * idx <= last) ;
3531+ & orig_slice[ first..last]
3532+ } else {
3533+ let ptr = kani:: any_where :: < usize , _ > ( |val| * val != 0 ) as * const T ;
3534+ kani:: assume ( ptr. is_aligned ( ) ) ;
3535+ unsafe { crate :: slice:: from_raw_parts ( ptr, 0 ) }
3536+ }
3537+ }
3538+
3539+ fn any_iter < ' a , T > ( orig_slice : & ' a [ T ] ) -> Iter < ' a , T > {
3540+ let slice = any_slice ( orig_slice) ;
3541+ Iter :: new ( slice)
3542+ }
3543+
3544+ /// Macro that generates a harness for a given `Iter` method.
3545+ ///
3546+ /// Takes the name of the harness, the element type, and an expression to check.
3547+ macro_rules! check_safe_abstraction {
3548+ ( $harness: ident, $elem_ty: ty, $call: expr) => {
3549+ #[ kani:: proof]
3550+ fn $harness( ) {
3551+ let array: [ $elem_ty; MAX_LEN ] = kani:: any( ) ;
3552+ let mut iter = any_iter:: <$elem_ty>( & array) ;
3553+ let target = $call;
3554+ target( & mut iter) ;
3555+ kani:: assert( iter. is_safe( ) , "Iter is safe" ) ;
3556+ }
3557+ } ;
3558+ }
3559+
3560+ /// Macro that generates a harness for a given unsafe `Iter` method.
3561+ ///
3562+ /// Takes:
3563+ /// 1. The name of the harness
3564+ /// 2. the element type
3565+ /// 3. the target function
3566+ /// 4. the optional arguments.
3567+ macro_rules! check_unsafe_contracts {
3568+ ( $harness: ident, $elem_ty: ty, $func: ident( $( $args: expr) ,* ) ) => {
3569+ #[ kani:: proof_for_contract( Iter :: $func) ]
3570+ fn $harness( ) {
3571+ let array: [ $elem_ty; MAX_LEN ] = kani:: any( ) ;
3572+ let mut iter = any_iter:: <$elem_ty>( & array) ;
3573+ let _ = unsafe { iter. $func( $( $args) ,* ) } ;
3574+ }
3575+ } ;
3576+ }
3577+
3578+ macro_rules! check_iter_with_ty {
3579+ ( $module: ident, $ty: ty, $max: expr) => {
3580+ mod $module {
3581+ use super :: * ;
3582+ const MAX_LEN : usize = $max;
3583+
3584+ #[ kani:: proof]
3585+ fn check_new_iter( ) {
3586+ let array: [ $ty; MAX_LEN ] = kani:: any( ) ;
3587+ let slice = any_slice:: <$ty>( & array) ;
3588+ let mut iter = Iter :: new( slice) ;
3589+ kani:: assert( iter. is_safe( ) , "Iter is safe" ) ;
3590+ }
3591+
3592+ /// Count consumes the value, thus, invoke it directly.
3593+ #[ kani:: proof]
3594+ fn check_count( ) {
3595+ let array: [ $ty; MAX_LEN ] = kani:: any( ) ;
3596+ let mut iter = any_iter:: <$ty>( & array) ;
3597+ iter. count( ) ;
3598+ }
3599+
3600+ #[ kani:: proof]
3601+ fn check_default( ) {
3602+ let iter: Iter <' _, $ty> = Iter :: default ( ) ;
3603+ kani:: assert( iter. is_safe( ) , "Iter is safe" ) ;
3604+ }
3605+
3606+ check_unsafe_contracts!( check_next_back_unchecked, $ty, next_back_unchecked( ) ) ;
3607+ //check_unsafe_contracts!(check_post_inc_start, $ty, post_inc_start(kani::any()));
3608+ //check_unsafe_contracts!(check_pre_dec_end, $ty, pre_dec_end(kani::any()));
3609+
3610+ // FIXME: The functions that are commented out are currently failing verification.
3611+ // Debugging the issue is currently blocked by:
3612+ // https://github.com/model-checking/kani/issues/3670
3613+ //
3614+ // Public functions that call safe abstraction `make_slice`.
3615+ // check_safe_abstraction!(check_as_slice, $ty, as_slice);
3616+ // check_safe_abstraction!(check_as_ref, $ty, as_ref);
3617+
3618+ // check_safe_abstraction!(check_advance_back_by, $ty, advance_back_by, kani::any());
3619+
3620+ check_safe_abstraction!( check_is_empty, $ty, |iter: & mut Iter <' _, $ty>| {
3621+ let _ = iter. is_empty( ) ;
3622+ } ) ;
3623+ check_safe_abstraction!( check_len, $ty, |iter: & mut Iter <' _, $ty>| {
3624+ let _ = iter. len( ) ;
3625+ } ) ;
3626+ check_safe_abstraction!( check_size_hint, $ty, |iter: & mut Iter <' _, $ty>| {
3627+ let _ = iter. size_hint( ) ;
3628+ } ) ;
3629+ //check_safe_abstraction!(check_nth, $ty, |iter: &mut Iter<'_, $ty>| { let _ = iter.nth(kani::any()); });
3630+ //check_safe_abstraction!(check_advance_by, $ty, |iter: &mut Iter<'_, $ty>| { let _ = iter.advance_by(kani::any()); });
3631+ check_safe_abstraction!( check_next_back, $ty, |iter: & mut Iter <' _, $ty>| {
3632+ let _ = iter. next_back( ) ;
3633+ } ) ;
3634+ //check_safe_abstraction!(check_nth_back, $ty, |iter: &mut Iter<'_, $ty>| { let _ = iter.nth_back(kani::any()); });
3635+ check_safe_abstraction!( check_next, $ty, |iter: & mut Iter <' _, $ty>| {
3636+ let _ = iter. next( ) ;
3637+ } ) ;
3638+
3639+ // Ensure that clone always generates a safe object.
3640+ check_safe_abstraction!( check_clone, $ty, |iter: & mut Iter <' _, $ty>| {
3641+ kani:: assert( iter. clone( ) . is_safe( ) , "Clone is safe" ) ;
3642+ } ) ;
3643+ }
3644+ } ;
3645+ }
3646+
3647+ // FIXME: Add harnesses for ZST with alignment > 1.
3648+ check_iter_with_ty ! ( verify_unit, ( ) , isize :: MAX as usize ) ;
3649+ check_iter_with_ty ! ( verify_u8, u8 , u32 :: MAX as usize ) ;
3650+ check_iter_with_ty ! ( verify_char, char , 50 ) ;
3651+ check_iter_with_ty ! ( verify_tup, ( char , u8 ) , 50 ) ;
3652+ }
0 commit comments