Skip to content

Commit

Permalink
Merge branch 'main' into usage_proofs
Browse files Browse the repository at this point in the history
  • Loading branch information
stogaru authored Dec 12, 2024
2 parents ed1e26e + 27a9931 commit 991a76b
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 2 deletions.
2 changes: 1 addition & 1 deletion doc/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
- [4: Memory safety of BTreeMap's `btree::node` module](./challenges/0004-btree-node.md)
- [5: Verify functions iterating over inductive data type: `linked_list`](./challenges/0005-linked-list.md)
- [6: Safety of `NonNull`](./challenges/0006-nonnull.md)
- [7: Safety of Methods for Atomic Types and `ReentrantLock`](./challenges/0007-atomic-types.md)
- [7: Safety of Methods for Atomic Types & Atomic Intrinsics](./challenges/0007-atomic-types.md)
- [8: Contracts for SmallSort](./challenges/0008-smallsort.md)
- [9: Safe abstractions for `core::time::Duration`](./challenges/0009-duration.md)
- [10: Memory safety of String](./challenges/0010-string.md)
Expand Down
41 changes: 40 additions & 1 deletion library/core/src/ffi/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,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 {
const_eval_select!(
@capture { bytes: &[u8] } -> &CStr:
Expand Down Expand Up @@ -779,6 +783,8 @@ impl AsRef<CStr> for CStr {
#[unstable(feature = "cstr_internals", issue = "none")]
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_cstr_from_ptr", since = "1.81.0"))]
#[rustc_allow_const_fn_unstable(const_eval_select)]
#[requires(is_null_terminated(ptr))]
#[ensures(|&result| result < isize::MAX as usize && unsafe { *ptr.add(result) } == 0)]
const unsafe fn strlen(ptr: *const c_char) -> usize {
const_eval_select!(
@capture { s: *const c_char = ptr } -> usize:
Expand Down Expand Up @@ -910,6 +916,25 @@ mod verify {
}
}

// 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]);
}

// pub fn bytes(&self) -> Bytes<'_>
#[kani::proof]
#[kani::unwind(32)]
Expand Down Expand Up @@ -972,6 +997,7 @@ mod verify {
assert!(c_str.is_safe());
}

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

// const unsafe fn strlen(ptr: *const c_char) -> usize
#[kani::proof_for_contract(super::strlen)]
#[kani::unwind(33)]
fn check_strlen_contract() {
const MAX_SIZE: usize = 32;
let mut string: [u8; MAX_SIZE] = kani::any();
let ptr = string.as_ptr() as *const c_char;

unsafe { super::strlen(ptr); }
}

// pub const unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr
#[kani::proof_for_contract(CStr::from_ptr)]
#[kani::unwind(33)]
fn check_from_ptr_contract() {
Expand All @@ -1052,6 +1090,7 @@ mod verify {
unsafe { CStr::from_ptr(ptr); }
}

// pub const fn is_empty(&self) -> bool
#[kani::proof]
#[kani::unwind(33)]
fn check_is_empty() {
Expand All @@ -1064,5 +1103,5 @@ mod verify {
let expected_is_empty = bytes.len() == 0;
assert_eq!(expected_is_empty, c_str.is_empty());
assert!(c_str.is_safe());
}
}
}

0 comments on commit 991a76b

Please sign in to comment.