Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cstr verify safety contracts of unsafe functions strlen, from_bytes_with_nul_unchecked #193

Merged
merged 39 commits into from
Dec 12, 2024
Merged
Changes from 7 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
64ddbff
Add to_bytes and to_bytes_with_nul harnesses
Yenyun035 Nov 27, 2024
1edbe31
Merge branch 'main' into c-0013-yenyunw-cstr-to-bytes
Yenyun035 Nov 27, 2024
4059184
Merge branch 'main' into c-0013-yenyunw-cstr-to-bytes
Yenyun035 Nov 28, 2024
8b15d35
add bytes, to_str and as_ptr proofs
rajathkotyal Nov 28, 2024
ec0e94c
Update c_str.rs
rajathkotyal Nov 28, 2024
e70a1ef
add harness for `from_ptr`, `from_bytes_with_nul_unchecked`, `strlen`…
rajathkotyal Nov 28, 2024
7ddf9de
Fix contracts and harnesses
Yenyun035 Nov 28, 2024
937058a
Merge branch 'main' into c-0013-rajathm-part3
rajathkotyal Nov 30, 2024
0a372c1
update stubbing and const proof
rajathkotyal Dec 1, 2024
e323bf3
update stub and cstr compile time proof
rajathkotyal Dec 1, 2024
e054f35
comment
rajathkotyal Dec 1, 2024
8b95cb1
update cstr strlen
rajathkotyal Dec 2, 2024
eaf60eb
Merge branch 'main' into c-0013-rajathm-part3
rajathkotyal Dec 2, 2024
c54a51e
Update c_str.rs
rajathkotyal Dec 2, 2024
11c3f8d
Merge branch 'main' into c-0013-rajathm-part3
rajathkotyal Dec 3, 2024
00365ed
update pre and post conditions
rajathkotyal Dec 3, 2024
549613d
Merge branch 'main' into c-0013-rajathm-part3
rajathkotyal Dec 5, 2024
b21eb00
delimiter typo
rajathkotyal Dec 5, 2024
3f4be6d
retained only necessary checks pertaining to the function, cleaned up…
rajathkotyal Dec 5, 2024
076e972
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 5, 2024
2e3528b
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 6, 2024
407fe08
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 6, 2024
600c859
Merge branch 'main' into c-0013-rajathm-part3
rajathkotyal Dec 6, 2024
53588b4
removed redundant check
rajathkotyal Dec 6, 2024
689c9d0
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 7, 2024
dbf278b
Fix import
Yenyun035 Dec 7, 2024
4301961
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 8, 2024
f330bf4
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 10, 2024
8c47af3
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 10, 2024
e0ef0dc
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 10, 2024
c5d8da3
Fix is_null_terminated not use issue
Yenyun035 Dec 10, 2024
0f075fc
Fix unused import error
carolynzech Dec 10, 2024
e675a37
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 11, 2024
6d0a1f0
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 11, 2024
6d19642
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 11, 2024
2789d95
Fix format && comments
Yenyun035 Dec 11, 2024
23c66a3
Formatting
Yenyun035 Dec 11, 2024
51462ff
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 11, 2024
0521c6b
Merge branch 'main' into c-0013-rajathm-part3
Yenyun035 Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 85 additions & 2 deletions library/core/src/ffi/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::slice::memchr;
use crate::{fmt, intrinsics, ops, slice, str};

use crate::ub_checks::Invariant;
use safety::{requires, ensures};

#[cfg(kani)]
use crate::kani;
Expand Down Expand Up @@ -431,6 +432,10 @@ impl CStr {
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
#[rustc_const_stable(feature = "const_cstr_unchecked", since = "1.59.0")]
#[rustc_allow_const_fn_unstable(const_eval_select)]
// Preconditions: Null-terminated and no intermediate null bytes
#[requires(!bytes.is_empty() && bytes[bytes.len() - 1] == 0 && !bytes[..bytes.len()-1].contains(&0))]
// Postcondition: The resulting CStr satisfies the same conditions as preconditions
#[ensures(|result| result.is_safe())]
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
#[inline]
fn rt_impl(bytes: &[u8]) -> &CStr {
Expand Down Expand Up @@ -860,7 +865,7 @@ impl FusedIterator for Bytes<'_> {}
mod verify {
use super::*;

// pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError>
// Proof harness for pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError>
#[kani::proof]
#[kani::unwind(32)] // 7.3 seconds when 16; 33.1 seconds when 32
fn check_from_bytes_until_nul() {
Expand All @@ -875,4 +880,82 @@ mod verify {
assert!(c_str.is_safe());
}
}
}

rajathkotyal marked this conversation as resolved.
Show resolved Hide resolved
// Stub implementation for strlen: https://model-checking.github.io/kani/rfc/rfcs/0002-function-stubbing.html
#[cfg(kani)]
unsafe fn stub_strlen(s: *const c_char) -> usize {
let mut len = 0;
unsafe {
while *s.add(len) != 0 {
len += 1;
}
}
len
}

// Proof harness for pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr
#[cfg(kani)]
#[kani::proof]
#[kani::unwind(33)]
#[kani::stub(strlen, stub_strlen)]
fn check_from_ptr() {
const MAX_SIZE: usize = 32;
//generating an arbitrary byte array with a max size
let mut string: [u8; MAX_SIZE] = kani::any();
// need At least one nul terminator is present within the array
let nul_position: usize = kani::any_where(|x| *x >= 0 && *x < MAX_SIZE);
string[nul_position] = 0;
// Make sure that there are no other nul bytes before `nul_position`
for i in 0..nul_position {
kani::assume(string[i] != 0);
}
// start from the beginning of the string
let ptr = string.as_ptr().cast::<c_char>();
let c_str = unsafe { CStr::from_ptr(ptr) };
// Compute the expected length up to the nul terminator
let len = unsafe { strlen(ptr) };
// Check that `c_str` has the correct length
assert_eq!(c_str.to_bytes().len(), len);
assert!(c_str.is_safe());
}

// Proof harness for const unsafe fn strlen(ptr: *const c_char) -> usize
#[cfg(kani)]
#[kani::proof]
#[kani::unwind(32)]
#[kani::stub(strlen, stub_strlen)]
fn check_strlen() {
const MAX_SIZE: usize = 32;
let mut string: [u8; MAX_SIZE] = kani::any();
let nul_position: usize = kani::any_where(|x| *x >= 0 && *x < MAX_SIZE);
string[nul_position] = 0;
rajathkotyal marked this conversation as resolved.
Show resolved Hide resolved
// Make sure that there are no other nul bytes before `nul_position`
for i in 0..nul_position {
kani::assume(string[i] != 0);
}
let ptr = string.as_ptr().cast::<c_char>();
// Call strlen with the pointer
let len = unsafe { strlen(ptr) };
// Check that len matches the expected position of the nul terminator
assert_eq!(len, nul_position);
}

// pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr
#[kani::proof_for_contract(CStr::from_bytes_with_nul_unchecked)]
#[kani::unwind(33)]
fn check_from_bytes_with_nul_unchecked() {
const MAX_SIZE: usize = 32;
let string: [u8; MAX_SIZE] = kani::any();
let slice = kani::slice::any_slice_of_array(&string);

// Kani assumes that the input slice is null-terminated and contains
// no intermediate null bytes
let c_str = unsafe { CStr::from_bytes_with_nul_unchecked(slice) };
// Kani ensures that the output CStr holds the CStr safety invariant

// Correctness check
let bytes = c_str.to_bytes();
let len = bytes.len();
assert_eq!(bytes, &slice[..len]);
}
}