Skip to content

Commit f375ace

Browse files
committed
[kani] Use function contracts instead of proofs
gherrit-pr-id: I681bacc2fbc00531c8493490b9f982fa4aea307a
1 parent d769fb9 commit f375ace

File tree

3 files changed

+33
-52
lines changed

3 files changed

+33
-52
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ jobs:
592592
# TODO(https://github.com/model-checking/kani-github-action/issues/56):
593593
# Go back to testing all features once the Kani GitHub Action supports
594594
# specifying a particular toolchain.
595-
args: "--package zerocopy --features __internal_use_only_features_that_work_on_stable --output-format=terse --randomize-layout --memory-safety-checks --overflow-checks --undefined-function-checks --unwinding-checks"
595+
args: "--package zerocopy --features __internal_use_only_features_that_work_on_stable --output-format=terse -Zfunction-contracts --randomize-layout --memory-safety-checks --overflow-checks --undefined-function-checks --unwinding-checks"
596596
# This version is automatically rolled by
597597
# `roll-pinned-toolchain-versions.yml`.
598598
kani-version: 0.55.0

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ extern crate alloc;
382382
#[cfg(any(feature = "alloc", test))]
383383
use alloc::{boxed::Box, vec::Vec};
384384

385-
#[cfg(any(feature = "alloc", test, kani))]
385+
#[cfg(any(feature = "alloc", test))]
386386
use core::alloc::Layout;
387387

388388
// Used by `TryFromBytes::is_bit_valid`.

src/util/mod.rs

Lines changed: 31 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,21 @@ pub(crate) fn validate_aligned_to<T: AsAddress, U>(t: T) -> Result<(), Alignment
116116
///
117117
/// This function assumes that align is a power of two; there are no guarantees
118118
/// on the answer it gives if this is not the case.
119+
#[cfg_attr(
120+
kani,
121+
kani::requires(len <= isize::MAX as usize),
122+
kani::requires(align.is_power_of_two()),
123+
kani::ensures(|&p| (len + p) % align.get() == 0),
124+
// Ensures that we add the minimum required padding.
125+
kani::ensures(|&p| p < align.get()),
126+
)]
119127
pub(crate) const fn padding_needed_for(len: usize, align: NonZeroUsize) -> usize {
128+
#[cfg(kani)]
129+
#[kani::proof_for_contract(padding_needed_for)]
130+
fn proof() {
131+
padding_needed_for(kani::any(), kani::any());
132+
}
133+
120134
// Abstractly, we want to compute:
121135
// align - (len % align).
122136
// Handling the case where len%align is 0.
@@ -178,10 +192,27 @@ pub(crate) const fn padding_needed_for(len: usize, align: NonZeroUsize) -> usize
178192
/// May panic if `align` is not a power of two. Even if it doesn't panic in this
179193
/// case, it will produce nonsense results.
180194
#[inline(always)]
195+
#[cfg_attr(
196+
kani,
197+
kani::requires(align.is_power_of_two()),
198+
kani::ensures(|&m| m <= n && m % align.get() == 0),
199+
// Guarantees that `m` is the *largest* value such that `m % align == 0`.
200+
kani::ensures(|&m| {
201+
// If this `checked_add` fails, then the next multiple would wrap
202+
// around, which trivially satisfies the "largest value" requirement.
203+
m.checked_add(align.get()).map(|next_mul| next_mul > n).unwrap_or(true)
204+
})
205+
)]
181206
pub(crate) const fn round_down_to_next_multiple_of_alignment(
182207
n: usize,
183208
align: NonZeroUsize,
184209
) -> usize {
210+
#[cfg(kani)]
211+
#[kani::proof_for_contract(round_down_to_next_multiple_of_alignment)]
212+
fn proof() {
213+
round_down_to_next_multiple_of_alignment(kani::any(), kani::any());
214+
}
215+
185216
let align = align.get();
186217
#[cfg(zerocopy_panic_in_const_and_vec_try_reserve_1_57_0)]
187218
debug_assert!(align.is_power_of_two());
@@ -568,53 +599,3 @@ mod tests {
568599
round_down_to_next_multiple_of_alignment(0, NonZeroUsize::new(3).unwrap());
569600
}
570601
}
571-
572-
#[cfg(kani)]
573-
mod proofs {
574-
use super::*;
575-
576-
#[kani::proof]
577-
fn prove_round_down_to_next_multiple_of_alignment() {
578-
fn model_impl(n: usize, align: NonZeroUsize) -> usize {
579-
assert!(align.get().is_power_of_two());
580-
let mul = n / align.get();
581-
mul * align.get()
582-
}
583-
584-
let align: NonZeroUsize = kani::any();
585-
kani::assume(align.get().is_power_of_two());
586-
let n: usize = kani::any();
587-
588-
let expected = model_impl(n, align);
589-
let actual = round_down_to_next_multiple_of_alignment(n, align);
590-
assert_eq!(expected, actual, "round_down_to_next_multiple_of_alignment({}, {})", n, align);
591-
}
592-
593-
// Restricted to nightly since we use the unstable `usize::next_multiple_of`
594-
// in our model implementation.
595-
#[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)]
596-
#[kani::proof]
597-
fn prove_padding_needed_for() {
598-
fn model_impl(len: usize, align: NonZeroUsize) -> usize {
599-
let padded = len.next_multiple_of(align.get());
600-
let padding = padded - len;
601-
padding
602-
}
603-
604-
let align: NonZeroUsize = kani::any();
605-
kani::assume(align.get().is_power_of_two());
606-
let len: usize = kani::any();
607-
// Constrain `len` to valid Rust lengths, since our model implementation
608-
// isn't robust to overflow.
609-
kani::assume(len <= isize::MAX as usize);
610-
kani::assume(align.get() < 1 << 29);
611-
612-
let expected = model_impl(len, align);
613-
let actual = padding_needed_for(len, align);
614-
assert_eq!(expected, actual, "padding_needed_for({}, {})", len, align);
615-
616-
let padded_len = actual + len;
617-
assert_eq!(padded_len % align, 0);
618-
assert!(padded_len / align >= len / align);
619-
}
620-
}

0 commit comments

Comments
 (0)