Skip to content

Commit eb21f69

Browse files
rajathkotyalYenyun035carolynzech
authored
Cstr verify safety contracts of unsafe functions strlen, from_bytes_with_nul_unchecked (#193)
Towards #150 Annotates and verifies the safety contracts for the following unsafe functions: `from_bytes_with_nul_uncheked` `core::ffi::c_str` `strlen` `core::ffi::c_str` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Yenyun035 <[email protected]> Co-authored-by: Yenyun035 <[email protected]> Co-authored-by: Carolyn Zech <[email protected]>
1 parent 0555537 commit eb21f69

File tree

1 file changed

+40
-1
lines changed

1 file changed

+40
-1
lines changed

library/core/src/ffi/c_str.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,10 @@ impl CStr {
456456
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
457457
#[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")]
458458
#[rustc_allow_const_fn_unstable(const_eval_select)]
459+
// Preconditions: Null-terminated and no intermediate null bytes
460+
#[requires(!bytes.is_empty() && bytes[bytes.len() - 1] == 0 && !bytes[..bytes.len()-1].contains(&0))]
461+
// Postcondition: The resulting CStr satisfies the same conditions as preconditions
462+
#[ensures(|result| result.is_safe())]
459463
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
460464
const_eval_select!(
461465
@capture { bytes: &[u8] } -> &CStr:
@@ -779,6 +783,8 @@ impl AsRef<CStr> for CStr {
779783
#[unstable(feature = "cstr_internals", issue = "none")]
780784
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0"))]
781785
#[rustc_allow_const_fn_unstable(const_eval_select)]
786+
#[requires(is_null_terminated(ptr))]
787+
#[ensures(|&result| result < isize::MAX as usize && unsafe { *ptr.add(result) } == 0)]
782788
const unsafe fn strlen(ptr: *const c_char) -> usize {
783789
const_eval_select!(
784790
@capture { s: *const c_char = ptr } -> usize:
@@ -910,6 +916,25 @@ mod verify {
910916
}
911917
}
912918

919+
// pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr
920+
#[kani::proof_for_contract(CStr::from_bytes_with_nul_unchecked)]
921+
#[kani::unwind(33)]
922+
fn check_from_bytes_with_nul_unchecked() {
923+
const MAX_SIZE: usize = 32;
924+
let string: [u8; MAX_SIZE] = kani::any();
925+
let slice = kani::slice::any_slice_of_array(&string);
926+
927+
// Kani assumes that the input slice is null-terminated and contains
928+
// no intermediate null bytes
929+
let c_str = unsafe { CStr::from_bytes_with_nul_unchecked(slice) };
930+
// Kani ensures that the output CStr holds the CStr safety invariant
931+
932+
// Correctness check
933+
let bytes = c_str.to_bytes();
934+
let len = bytes.len();
935+
assert_eq!(bytes, &slice[..len]);
936+
}
937+
913938
// pub fn bytes(&self) -> Bytes<'_>
914939
#[kani::proof]
915940
#[kani::unwind(32)]
@@ -972,6 +997,7 @@ mod verify {
972997
assert!(c_str.is_safe());
973998
}
974999

1000+
// pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError>
9751001
#[kani::proof]
9761002
#[kani::unwind(17)]
9771003
fn check_from_bytes_with_nul() {
@@ -1042,6 +1068,18 @@ mod verify {
10421068
assert!(c_str.is_safe());
10431069
}
10441070

1071+
// const unsafe fn strlen(ptr: *const c_char) -> usize
1072+
#[kani::proof_for_contract(super::strlen)]
1073+
#[kani::unwind(33)]
1074+
fn check_strlen_contract() {
1075+
const MAX_SIZE: usize = 32;
1076+
let mut string: [u8; MAX_SIZE] = kani::any();
1077+
let ptr = string.as_ptr() as *const c_char;
1078+
1079+
unsafe { super::strlen(ptr); }
1080+
}
1081+
1082+
// pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr
10451083
#[kani::proof_for_contract(CStr::from_ptr)]
10461084
#[kani::unwind(33)]
10471085
fn check_from_ptr_contract() {
@@ -1052,6 +1090,7 @@ mod verify {
10521090
unsafe { CStr::from_ptr(ptr); }
10531091
}
10541092

1093+
// pub const fn is_empty(&self) -> bool
10551094
#[kani::proof]
10561095
#[kani::unwind(33)]
10571096
fn check_is_empty() {
@@ -1064,5 +1103,5 @@ mod verify {
10641103
let expected_is_empty = bytes.len() == 0;
10651104
assert_eq!(expected_is_empty, c_str.is_empty());
10661105
assert!(c_str.is_safe());
1067-
}
1106+
}
10681107
}

0 commit comments

Comments
 (0)