Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 1 addition & 1 deletion library/core/src/ffi/c_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ mod verify {

// 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
#[kani::unwind(33)]
fn check_from_bytes_until_nul() {
const MAX_SIZE: usize = 32;
let string: [u8; MAX_SIZE] = kani::any();
Expand Down
51 changes: 46 additions & 5 deletions library/core/src/slice/memchr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Original implementation taken from rust-memchr.
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch

#[cfg(kani)]
use crate::kani;
use crate::intrinsics::const_eval_select;

const LO_USIZE: usize = usize::repeat_u8(0x01);
Expand All @@ -23,12 +25,22 @@ const fn contains_zero_byte(x: usize) -> bool {
#[inline]
#[must_use]
pub const fn memchr(x: u8, text: &[u8]) -> Option<usize> {
// Fast path for small slices.
if text.len() < 2 * USIZE_BYTES {
return memchr_naive(x, text);
}
// For Kani, always use the byte-by-byte loop: it is semantically
// equivalent to the word-at-a-time `memchr_aligned` (see comment there)
// but symbolically executes with a fraction of the cost, and `memchr` is
// reached from many contract clauses (e.g. `CStr::is_safe`).
#[cfg(kani)]
return memchr_naive(x, text);

#[cfg(not(kani))]
{
// Fast path for small slices.
if text.len() < 2 * USIZE_BYTES {
return memchr_naive(x, text);
}

memchr_aligned(x, text)
memchr_aligned(x, text)
}
}

#[inline]
Expand Down Expand Up @@ -159,3 +171,32 @@ pub fn memrchr(x: u8, text: &[u8]) -> Option<usize> {
// Find the byte before the point the body loop stopped.
text[..offset].iter().rposition(|elt| *elt == x)
}

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
pub mod verify {
Comment thread
feliperodri marked this conversation as resolved.
use super::*;

/// With `cfg(kani)`, `memchr` always takes the `memchr_naive` path (see
/// comment there), so the word-at-a-time `memchr_aligned` is no longer
/// exercised by the harnesses of its callers (e.g. `CStr`). This harness
/// keeps `memchr_aligned` covered by checking it against `memchr_naive`
/// for all inputs up to MAX_SIZE, including all UB checks in its unsafe
/// word-sized reads.
///
/// MAX_SIZE needs to exceed 2 * size_of::<usize>() so that the aligned
/// word-scanning loop is reachable (smaller inputs take an early naive
/// path inside `memchr_aligned`'s compile-time branch counterpart).
#[kani::proof]
#[kani::unwind(26)]
pub fn check_memchr_aligned_equiv_naive() {
const MAX_SIZE: usize = 24;
let x: u8 = kani::any();
let text: [u8; MAX_SIZE] = kani::any();
let slice = kani::slice::any_slice_of_array(&text);
// Implicit precondition of `memchr_aligned`, established by its only
// caller `memchr`: short slices are handled by `memchr_naive` there.
kani::assume(slice.len() >= 2 * USIZE_BYTES);
assert_eq!(memchr_aligned(x, slice), memchr_naive(x, slice));
}
}
Loading