@@ -8,11 +8,14 @@ use crate::hint::assert_unchecked;
8
8
use crate :: iter:: {
9
9
FusedIterator , TrustedLen , TrustedRandomAccess , TrustedRandomAccessNoCoerce , UncheckedIterator ,
10
10
} ;
11
+ #[ cfg( kani) ]
12
+ use crate :: kani;
11
13
use crate :: marker:: PhantomData ;
12
14
use crate :: mem:: { self , SizedTypeProperties } ;
13
15
use crate :: num:: NonZero ;
14
16
use crate :: ptr:: { NonNull , without_provenance, without_provenance_mut} ;
15
17
use crate :: { cmp, fmt} ;
18
+ use crate :: ub_checks:: Invariant ;
16
19
17
20
#[ stable( feature = "boxed_slice_into_iter" , since = "1.80.0" ) ]
18
21
impl < T > !Iterator for [ T ] { }
@@ -157,6 +160,32 @@ impl<T> AsRef<[T]> for Iter<'_, T> {
157
160
}
158
161
}
159
162
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
+
160
189
/// Mutable slice iterator.
161
190
///
162
191
/// This struct is created by the [`iter_mut`] method on [slices].
@@ -196,6 +225,29 @@ pub struct IterMut<'a, T: 'a> {
196
225
_marker : PhantomData < & ' a mut T > ,
197
226
}
198
227
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
+
199
251
#[ stable( feature = "core_impl_debug" , since = "1.9.0" ) ]
200
252
impl < T : fmt:: Debug > fmt:: Debug for IterMut < ' _ , T > {
201
253
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> {
3464
3516
f. debug_struct ( "ChunkByMut" ) . field ( "slice" , & self . slice ) . finish ( )
3465
3517
}
3466
3518
}
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