Skip to content

Commit 82da845

Browse files
authored
CStr Safety invariant & Harnesses for from_bytes_until_nul (#180)
Towards #150 ### Changes * Added a `CStr` Safety Invariant * Added a harness for `from_bytes_until_nul`, the harness covers: * The input slice contains a single null byte at the end; * The input slice contains no null bytes; * The input slice contains intermediate null bytes ### Discussion * [Safety invariant implementation](#150 (comment)) * [Input array generation](#181) ### Verification Result `./scripts/run-kani.sh --kani-args --harness ffi::c_str::verify` ``` // array size 16 Checking harness ffi::c_str::verify::check_from_bytes_until_nul... VERIFICATION RESULT: ** 0 of 140 failed (5 unreachable) VERIFICATION:- SUCCESSFUL Verification Time: 7.3023376s Complete - 1 successfully verified harnesses, 0 failures, 1 total. ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
1 parent 07318df commit 82da845

File tree

1 file changed

+43
-0
lines changed

1 file changed

+43
-0
lines changed

library/core/src/ffi/c_str.rs

+43
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ use crate::ptr::NonNull;
99
use crate::slice::memchr;
1010
use crate::{fmt, intrinsics, ops, slice, str};
1111

12+
use crate::ub_checks::Invariant;
13+
14+
#[cfg(kani)]
15+
use crate::kani;
16+
1217
// FIXME: because this is doc(inline)d, we *have* to use intra-doc links because the actual link
1318
// depends on where the item is being documented. however, since this is libcore, we can't
1419
// actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the
@@ -207,6 +212,22 @@ impl fmt::Display for FromBytesWithNulError {
207212
}
208213
}
209214

215+
#[unstable(feature = "ub_checks", issue = "none")]
216+
impl Invariant for &CStr {
217+
/**
218+
* Safety invariant of a valid CStr:
219+
* 1. An empty CStr should have a null byte.
220+
* 2. A valid CStr should end with a null-terminator and contains
221+
* no intermediate null bytes.
222+
*/
223+
fn is_safe(&self) -> bool {
224+
let bytes: &[c_char] = &self.inner;
225+
let len = bytes.len();
226+
227+
!bytes.is_empty() && bytes[len - 1] == 0 && !bytes[..len-1].contains(&0)
228+
}
229+
}
230+
210231
impl CStr {
211232
/// Wraps a raw C string with a safe C string wrapper.
212233
///
@@ -833,3 +854,25 @@ impl Iterator for Bytes<'_> {
833854

834855
#[unstable(feature = "cstr_bytes", issue = "112115")]
835856
impl FusedIterator for Bytes<'_> {}
857+
858+
#[cfg(kani)]
859+
#[unstable(feature = "kani", issue = "none")]
860+
mod verify {
861+
use super::*;
862+
863+
// pub const fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError>
864+
#[kani::proof]
865+
#[kani::unwind(32)] // 7.3 seconds when 16; 33.1 seconds when 32
866+
fn check_from_bytes_until_nul() {
867+
const MAX_SIZE: usize = 32;
868+
let string: [u8; MAX_SIZE] = kani::any();
869+
// Covers the case of a single null byte at the end, no null bytes, as
870+
// well as intermediate null bytes
871+
let slice = kani::slice::any_slice_of_array(&string);
872+
873+
let result = CStr::from_bytes_until_nul(slice);
874+
if let Ok(c_str) = result {
875+
assert!(c_str.is_safe());
876+
}
877+
}
878+
}

0 commit comments

Comments
 (0)