@@ -10,6 +10,7 @@ use crate::slice::memchr;
10
10
use crate :: { fmt, intrinsics, ops, slice, str} ;
11
11
12
12
// use safety::{requires, ensures};
13
+ use crate :: ub_checks:: Invariant ;
13
14
14
15
#[ cfg( kani) ]
15
16
use crate :: kani;
@@ -105,9 +106,6 @@ use crate::kani;
105
106
// want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under
106
107
// `cfg(doc)`. This is an ad-hoc implementation of attribute privacy.
107
108
#[ repr( transparent) ]
108
- #[ derive( kani:: Arbitrary ) ]
109
- #[ derive( kani:: Invariant ) ]
110
- #[ safety_constraint( inner. len( ) > 0 && inner[ inner. len( ) - 1 ] == 0 && !inner[ ..inner. len( ) - 1 ] . contains( & 0 ) ) ]
111
109
pub struct CStr {
112
110
// FIXME: this should not be represented with a DST slice but rather with
113
111
// just a raw `c_char` along with some form of marker to make
@@ -215,6 +213,23 @@ impl fmt::Display for FromBytesWithNulError {
215
213
}
216
214
}
217
215
216
+ #[ unstable( feature = "ub_checks" , issue = "none" ) ]
217
+ impl Invariant for & CStr {
218
+ fn is_safe ( & self ) -> bool {
219
+ let bytes: & [ c_char ] = & self . inner ;
220
+ let len = bytes. len ( ) ;
221
+
222
+ // An empty CStr should has a null byte.
223
+ // A valid CStr should end with a null-terminator and contains
224
+ // no intermediate null bytes.
225
+ if bytes. is_empty ( ) || bytes[ len - 1 ] != 0 || bytes[ ..len-1 ] . contains ( & 0 ) {
226
+ return false ;
227
+ }
228
+
229
+ true
230
+ }
231
+ }
232
+
218
233
impl CStr {
219
234
/// Wraps a raw C string with a safe C string wrapper.
220
235
///
@@ -841,3 +856,49 @@ impl Iterator for Bytes<'_> {
841
856
842
857
#[ unstable( feature = "cstr_bytes" , issue = "112115" ) ]
843
858
impl FusedIterator for Bytes < ' _ > { }
859
+
860
+ #[ cfg( kani) ]
861
+ #[ unstable( feature = "kani" , issue = "none" ) ]
862
+ mod verify {
863
+ use super :: * ;
864
+
865
+ // pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError>
866
+ #[ kani:: proof]
867
+ #[ kani:: unwind( 32 ) ] // Proof bounded by array length
868
+ fn check_from_bytes_until_nul_random_nul_byte ( ) {
869
+ const ARR_LEN : usize = 1000 ;
870
+ let mut string: [ u8 ; ARR_LEN ] = kani:: any ( ) ;
871
+
872
+ // ensure that there is at least one null byte
873
+ let idx: usize = kani:: any_where ( |x : & usize | * x >= 0 && * x < ARR_LEN ) ;
874
+ string[ idx] = 0 ;
875
+
876
+ let c_str = CStr :: from_bytes_until_nul ( & string) . unwrap ( ) ;
877
+ assert ! ( c_str. is_safe( ) ) ;
878
+ }
879
+
880
+ #[ kani:: proof]
881
+ #[ kani:: unwind( 32 ) ] // Proof bounded by array length
882
+ fn check_from_bytes_until_nul_single_nul_byte_end ( ) {
883
+ const ARR_LEN : usize = 1000 ;
884
+ // ensure that the string does not have intermediate null bytes
885
+ let mut string: [ u8 ; ARR_LEN ] = kani:: any_where ( |x : & [ u8 ; ARR_LEN ] | !x[ ..ARR_LEN -1 ] . contains ( & 0 ) ) ;
886
+ // ensure that the string is properly null-terminated
887
+ string[ ARR_LEN - 1 ] = 0 ;
888
+
889
+ let c_str = CStr :: from_bytes_until_nul ( & string) . unwrap ( ) ;
890
+ assert ! ( c_str. is_safe( ) ) ;
891
+ }
892
+
893
+ #[ kani:: proof]
894
+ #[ kani:: unwind( 8 ) ] // Proof bounded by array length
895
+ fn check_from_bytes_until_nul_single_nul_byte_head ( ) {
896
+ const ARR_LEN : usize = 8 ;
897
+ let mut string: [ u8 ; ARR_LEN ] = kani:: any ( ) ;
898
+ // The first byte is a null byte should result in an empty CStr.
899
+ string[ 0 ] = 0 ;
900
+
901
+ let c_str = CStr :: from_bytes_until_nul ( & string) . unwrap ( ) ;
902
+ assert ! ( c_str. is_safe( ) ) ;
903
+ }
904
+ }
0 commit comments