Skip to content

Commit

Permalink
Add loop contracts and harness for str::small_slice_eq
Browse files Browse the repository at this point in the history
  • Loading branch information
qinheping committed Oct 24, 2024
1 parent 61f68cf commit d3ef481
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@
#![feature(tbm_target_feature)]
#![feature(wasm_target_feature)]
#![feature(x86_amx_intrinsics)]
#![cfg_attr(kani, feature(proc_macro_hygiene))]
// tidy-alphabetical-end

// allow using `core::` in intra-doc links
Expand Down
50 changes: 49 additions & 1 deletion library/core/src/str/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ use crate::convert::TryInto as _;
use crate::slice::memchr;
use crate::{cmp, fmt};

use safety::{requires, ensures};

#[cfg(kani)]
use crate::kani;
#[cfg(kani)]
use crate::kani::mem::same_allocation;

// Pattern

/// A string pattern.
Expand Down Expand Up @@ -1880,8 +1887,9 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
/// # Safety
///
/// Both slices must have the same length.
#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] // only called on x86
#[cfg(all(target_arch = "x86_64", any(kani, target_feature = "sse2")))] // only called on x86
#[inline]
#[requires(x.len() == y.len())]
unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool {
debug_assert_eq!(x.len(), y.len());
// This function is adapted from
Expand Down Expand Up @@ -1926,6 +1934,10 @@ unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool {
unsafe {
let (mut px, mut py) = (x.as_ptr(), y.as_ptr());
let (pxend, pyend) = (px.add(x.len() - 4), py.add(y.len() - 4));
#[cfg_attr(kani, kani::loop_invariant(same_allocation(x.as_ptr(), px) && same_allocation(y.as_ptr(), py)
&& px as isize >= x.as_ptr() as isize
&& py as isize >= y.as_ptr() as isize
&& px as isize - x.as_ptr() as isize == (py as isize - y.as_ptr() as isize)))]
while px < pxend {
let vx = (px as *const u32).read_unaligned();
let vy = (py as *const u32).read_unaligned();
Expand All @@ -1940,3 +1952,39 @@ unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool {
vx == vy
}
}

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
pub mod verify {
use super::*;

// Copied from https://github.com/model-checking/kani/blob/main/library/kani/src/slice.rs
// should be removed when these functions are moved to `kani_core`
pub fn any_slice_of_array<T, const LENGTH: usize>(arr: &[T; LENGTH]) -> &[T] {
let (from, to) = any_range::<LENGTH>();
&arr[from..to]
}

fn any_range<const LENGTH: usize>() -> (usize, usize) {
let from: usize = kani::any();
let to: usize = kani::any();
kani::assume(to <= LENGTH);
kani::assume(from <= to);
(from, to)
}

#[cfg(all(kani, target_arch = "x86_64"))] // only called on x86
#[kani::proof]
#[kani::unwind(4)]
pub fn check_small_slice_eq() {
const ARR_SIZE: usize = 1000;
let x: [u8; ARR_SIZE] = kani::any();
let y: [u8; ARR_SIZE] = kani::any();
let xs = any_slice_of_array(&x);
let ys = any_slice_of_array(&y);
kani::assume(xs.len() == ys.len());
unsafe {
small_slice_eq(xs, ys);
}
}
}
2 changes: 1 addition & 1 deletion scripts/run-kani.sh
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ main() {

echo "Running Kani verify-std command..."

"$kani_path" verify-std -Z unstable-options ./library --target-dir "$temp_dir_target" -Z function-contracts -Z mem-predicates --output-format=terse $command_args
"$kani_path" verify-std -Z unstable-options ./library --target-dir "$temp_dir_target" -Z function-contracts -Z mem-predicates -Z loop-contracts --output-format=terse $command_args --enable-unstable --cbmc-args --object-bits 8
}

main
Expand Down

0 comments on commit d3ef481

Please sign in to comment.