diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 402c1e05b4400..7873a2a44c679 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -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 diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index 2f1096db8f00c..eda8ca17746dd 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -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. @@ -1880,8 +1887,9 @@ fn simd_contains(needle: &str, haystack: &str) -> Option { /// # 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 @@ -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(); @@ -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(arr: &[T; LENGTH]) -> &[T] { + let (from, to) = any_range::(); + &arr[from..to] + } + + fn any_range() -> (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); + } + } +} diff --git a/scripts/run-kani.sh b/scripts/run-kani.sh index 8ce27dac5d207..310ee6a1adf2c 100755 --- a/scripts/run-kani.sh +++ b/scripts/run-kani.sh @@ -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