Skip to content

Loop invariants and harnesses for memchr functions #429

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

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Draft
Changes from all 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
31 changes: 31 additions & 0 deletions library/core/src/slice/memchr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch

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

const LO_USIZE: usize = usize::repeat_u8(0x01);
const HI_USIZE: usize = usize::repeat_u8(0x80);
Expand Down Expand Up @@ -36,6 +38,7 @@ const fn memchr_naive(x: u8, text: &[u8]) -> Option<usize> {
let mut i = 0;

// FIXME(const-hack): Replace with `text.iter().pos(|c| *c == x)`.
#[kani::loop_invariant(i <= text.len() && kani::forall!(|j in (0,i)| unsafe {*text.as_ptr().wrapping_add(j)} != x))]
while i < text.len() {
if text[i] == x {
return Some(i);
Expand Down Expand Up @@ -78,6 +81,8 @@ const fn memchr_aligned(x: u8, text: &[u8]) -> Option<usize> {

// search the body of the text
let repeated_x = usize::repeat_u8(x);
#[kani::loop_invariant(len >= 2 * USIZE_BYTES && offset <= len &&
kani::forall!(|j in (0,offset)| unsafe {*text.as_ptr().wrapping_add(j)} != x))]
while offset <= len - 2 * USIZE_BYTES {
// SAFETY: the while's predicate guarantees a distance of at least 2 * usize_bytes
// between the offset and the end of the slice.
Expand Down Expand Up @@ -139,6 +144,7 @@ pub fn memrchr(x: u8, text: &[u8]) -> Option<usize> {
let repeated_x = usize::repeat_u8(x);
let chunk_bytes = size_of::<Chunk>();

#[kani::loop_invariant(true)]
while offset > min_aligned_offset {
// SAFETY: offset starts at len - suffix.len(), as long as it is greater than
// min_aligned_offset (prefix.len()) the remaining distance is at least 2 * chunk_bytes.
Expand All @@ -159,3 +165,28 @@ 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 {
use super::*;

#[kani::proof]
#[kani::solver(cvc5)]
#[cfg(not(all(target_arch = "x86_64", target_feature = "sse2")))]
pub fn check_memchr_naive() {
const ARR_SIZE: usize = 64;
let x: u8 = kani::any();
let text: [u8; ARR_SIZE] = kani::any();
let _result = memchr_naive(x, &text);
}

#[kani::proof]
#[cfg(not(all(target_arch = "x86_64", target_feature = "sse2")))]
pub fn check_memchr() {
const ARR_SIZE: usize = 64;
let x: u8 = kani::any();
let text: [u8; ARR_SIZE] = kani::any();
let _result = memrchr(x, &text);
}
}
Loading