Skip to content

Commit 0c70dcd

Browse files
qinhepingcelinval
andauthoredNov 8, 2024
Add loop contracts and harness for small_slice_eq (model-checking#122)
Porting from https://github.com/model-checking/kani/blob/main/tests/expected/loop-contract/small_slice_eq.rs The cbmc argument `--object-bits 8` is needed to avoid running out of memory for the harness. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Celina G. Val <celinval@amazon.com>

File tree

4 files changed

+64
-3
lines changed

4 files changed

+64
-3
lines changed
 

‎library/core/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@
230230
#![feature(unboxed_closures)]
231231
#![feature(unsized_fn_params)]
232232
#![feature(with_negative_coherence)]
233+
// Required for Kani loop contracts, which are annotated as custom stmt attributes.
234+
#![feature(proc_macro_hygiene)]
233235
// tidy-alphabetical-end
234236
//
235237
// Target features:

‎library/core/src/str/pattern.rs

+60-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ use crate::convert::TryInto as _;
4343
use crate::slice::memchr;
4444
use crate::{cmp, fmt};
4545

46+
#[cfg(all(target_arch = "x86_64", any(kani, target_feature = "sse2")))]
47+
use safety::{loop_invariant, requires};
48+
49+
#[cfg(kani)]
50+
use crate::kani;
51+
4652
// Pattern
4753

4854
/// A string pattern.
@@ -1905,8 +1911,9 @@ fn simd_contains(needle: &str, haystack: &str) -> Option<bool> {
19051911
/// # Safety
19061912
///
19071913
/// Both slices must have the same length.
1908-
#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] // only called on x86
1914+
#[cfg(all(target_arch = "x86_64", any(kani, target_feature = "sse2")))] // only called on x86
19091915
#[inline]
1916+
#[requires(x.len() == y.len())]
19101917
unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool {
19111918
debug_assert_eq!(x.len(), y.len());
19121919
// This function is adapted from
@@ -1951,6 +1958,11 @@ unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool {
19511958
unsafe {
19521959
let (mut px, mut py) = (x.as_ptr(), y.as_ptr());
19531960
let (pxend, pyend) = (px.add(x.len() - 4), py.add(y.len() - 4));
1961+
#[loop_invariant(crate::ub_checks::same_allocation(x.as_ptr(), px)
1962+
&& crate::ub_checks::same_allocation(y.as_ptr(), py)
1963+
&& px.addr() >= x.as_ptr().addr()
1964+
&& py.addr() >= y.as_ptr().addr()
1965+
&& px.addr() - x.as_ptr().addr() == py.addr() - y.as_ptr().addr())]
19541966
while px < pxend {
19551967
let vx = (px as *const u32).read_unaligned();
19561968
let vy = (py as *const u32).read_unaligned();
@@ -1965,3 +1977,50 @@ unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool {
19651977
vx == vy
19661978
}
19671979
}
1980+
1981+
#[cfg(kani)]
1982+
#[unstable(feature = "kani", issue = "none")]
1983+
pub mod verify {
1984+
use super::*;
1985+
1986+
#[cfg(all(kani, target_arch = "x86_64"))] // only called on x86
1987+
#[kani::proof]
1988+
#[kani::unwind(4)]
1989+
pub fn check_small_slice_eq() {
1990+
// TODO: ARR_SIZE can be `std::usize::MAX` with cbmc argument
1991+
// `--arrays-uf-always`
1992+
const ARR_SIZE: usize = 1000;
1993+
let x: [u8; ARR_SIZE] = kani::any();
1994+
let y: [u8; ARR_SIZE] = kani::any();
1995+
let xs = kani::slice::any_slice_of_array(&x);
1996+
let ys = kani::slice::any_slice_of_array(&y);
1997+
kani::assume(xs.len() == ys.len());
1998+
unsafe {
1999+
small_slice_eq(xs, ys);
2000+
}
2001+
}
2002+
2003+
/* This harness check `small_slice_eq` with dangling pointer to slice
2004+
with zero size. Kani finds safety issue of `small_slice_eq` in this
2005+
harness and hence the proof will fail.
2006+
2007+
#[cfg(all(kani, target_arch = "x86_64"))] // only called on x86
2008+
#[kani::proof]
2009+
#[kani::unwind(4)]
2010+
pub fn check_small_slice_eq_empty() {
2011+
let ptr_x = kani::any_where::<usize, _>(|val| *val != 0) as *const u8;
2012+
let ptr_y = kani::any_where::<usize, _>(|val| *val != 0) as *const u8;
2013+
kani::assume(ptr_x.is_aligned());
2014+
kani::assume(ptr_y.is_aligned());
2015+
assert_eq!(
2016+
unsafe {
2017+
small_slice_eq(
2018+
crate::slice::from_raw_parts(ptr_x, 0),
2019+
crate::slice::from_raw_parts(ptr_y, 0),
2020+
)
2021+
},
2022+
true
2023+
);
2024+
}
2025+
*/
2026+
}

‎scripts/run-kani.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ main() {
183183

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

186-
"$kani_path" verify-std -Z unstable-options ./library --target-dir "$temp_dir_target" -Z function-contracts -Z mem-predicates --output-format=terse $command_args
186+
"$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 12
187187
}
188188

189189
main

‎tool_config/kani-version.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
# incompatible with the verify-std repo.
33

44
[kani]
5-
commit = "2565ef65767a696f1d519b42621b4e502e8970d0"
5+
commit = "8400296f5280be4f99820129bc66447e8dff63f4"

0 commit comments

Comments
 (0)
Please sign in to comment.