Skip to content

Commit e151d22

Browse files
Deprecate any_raw and Invariant (rust-lang#1415)
* Add perf tests for kani::any() implementations Also change script to use release build instead of regular build. * Deprecate any_raw and Invariant Basically invariant type was only safe for T that had no invalid value independent on its binary values. All other types had some sort of UB involved. Thus, deprecate this for now. Note that we will likely want the Invariant type reintroduced one day, but we might need to either change its form or ensure that `any_raw()` at least respect `primitives` and `std` type invariants to avoid users having to explicitly check for any potential UB (which in itself is UB). Co-authored-by: Zyad Hassan <88045115+zhassan-aws@users.noreply.github.com>
1 parent 43cb18c commit e151d22

File tree

36 files changed

+282
-323
lines changed

36 files changed

+282
-323
lines changed

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,6 @@ exclude = [
5555
"tests/kani-dependency-test/dependency3",
5656
# cargo kani tests should also have their own workspace
5757
"tests/cargo-kani",
58+
"tests/perf",
59+
"tests/cargo-ui",
5860
]

kani-driver/src/call_cbmc.rs

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ impl KaniSession {
5454
// the best possible fix is port to rust instead of using python, or getting more
5555
// feedback than just exit status (or using a particular magic exit code?)
5656
}
57+
// TODO: We should print this even the verification fails but not if it crashes.
5758
println!("Verification Time: {}s", elapsed);
5859
}
5960

library/kani/src/arbitrary.rs

+81-18
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,102 @@
11
// Copyright Kani Contributors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
//! This module introduces the Arbitrary trait as well as implementation for the Invariant trait.
5-
use crate::{any_raw, assume, Invariant};
4+
//! This module introduces the Arbitrary trait as well as implementation for primitive types and
5+
//! other std containers.
6+
use std::num::*;
67

78
/// This trait should be used to generate symbolic variables that represent any valid value of
89
/// its type.
910
pub trait Arbitrary {
1011
fn any() -> Self;
1112
}
1213

13-
impl<T> Arbitrary for T
14-
where
15-
T: Invariant,
16-
{
17-
default fn any() -> Self {
18-
let value = unsafe { any_raw::<T>() };
19-
assume(value.is_valid());
20-
value
14+
/// The given type can be represented by an unconstrained symbolic value of size_of::<T>.
15+
macro_rules! trivial_arbitrary {
16+
( $type: ty ) => {
17+
impl Arbitrary for $type {
18+
#[inline(always)]
19+
fn any() -> Self {
20+
unsafe { crate::any_raw_internal::<$type>() }
21+
}
22+
}
23+
};
24+
}
25+
26+
trivial_arbitrary!(u8);
27+
trivial_arbitrary!(u16);
28+
trivial_arbitrary!(u32);
29+
trivial_arbitrary!(u64);
30+
trivial_arbitrary!(u128);
31+
trivial_arbitrary!(usize);
32+
33+
trivial_arbitrary!(i8);
34+
trivial_arbitrary!(i16);
35+
trivial_arbitrary!(i32);
36+
trivial_arbitrary!(i64);
37+
trivial_arbitrary!(i128);
38+
trivial_arbitrary!(isize);
39+
40+
// We do not constraint floating points values per type spec. Users must add assumptions to their
41+
// verification code if they want to eliminate NaN, infinite, or subnormal.
42+
trivial_arbitrary!(f32);
43+
trivial_arbitrary!(f64);
44+
45+
trivial_arbitrary!(());
46+
47+
impl Arbitrary for bool {
48+
#[inline(always)]
49+
fn any() -> Self {
50+
let byte = u8::any();
51+
byte == 0
52+
}
53+
}
54+
55+
/// Validate that a char is not outside the ranges [0x0, 0xD7FF] and [0xE000, 0x10FFFF]
56+
/// Ref: <https://doc.rust-lang.org/stable/nomicon/what-unsafe-does.html>
57+
impl Arbitrary for char {
58+
#[inline(always)]
59+
fn any() -> Self {
60+
// Generate an arbitrary u32 and constrain it to make it a valid representation of char.
61+
let val = u32::any();
62+
crate::assume(val <= 0xD7FF || (0xE000..=0x10FFFF).contains(&val));
63+
unsafe { char::from_u32_unchecked(val) }
2164
}
2265
}
2366

67+
macro_rules! nonzero_arbitrary {
68+
( $type: ty, $base: ty ) => {
69+
impl Arbitrary for $type {
70+
#[inline(always)]
71+
fn any() -> Self {
72+
let val = <$base>::any();
73+
crate::assume(val != 0);
74+
unsafe { <$type>::new_unchecked(val) }
75+
}
76+
}
77+
};
78+
}
79+
80+
nonzero_arbitrary!(NonZeroU8, u8);
81+
nonzero_arbitrary!(NonZeroU16, u16);
82+
nonzero_arbitrary!(NonZeroU32, u32);
83+
nonzero_arbitrary!(NonZeroU64, u64);
84+
nonzero_arbitrary!(NonZeroU128, u128);
85+
nonzero_arbitrary!(NonZeroUsize, usize);
86+
87+
nonzero_arbitrary!(NonZeroI8, i8);
88+
nonzero_arbitrary!(NonZeroI16, i16);
89+
nonzero_arbitrary!(NonZeroI32, i32);
90+
nonzero_arbitrary!(NonZeroI64, i64);
91+
nonzero_arbitrary!(NonZeroI128, i128);
92+
nonzero_arbitrary!(NonZeroIsize, isize);
93+
2494
impl<T, const N: usize> Arbitrary for [T; N]
2595
where
2696
T: Arbitrary,
2797
{
2898
fn any() -> Self {
29-
// The "correct way" would be to MaybeUninit but there is performance penalty.
30-
let mut data: [T; N] = unsafe { crate::any_raw() };
31-
32-
for elem in &mut data[..] {
33-
*elem = T::any();
34-
}
35-
36-
data
99+
[(); N].map(|_| T::any())
37100
}
38101
}
39102

library/kani/src/invariant.rs

+25-75
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// Copyright Kani Contributors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
//! This module introduces the Invariant trait as well as implementation for commonly used types.
5-
use std::num::*;
4+
//! This module introduces the Invariant trait.
5+
6+
use crate::Arbitrary;
67

78
/// Types that implement a check to ensure its value is valid and safe to be used. See
89
/// <https://doc.rust-lang.org/stable/nomicon/what-unsafe-does.html> for examples of valid values.
@@ -14,84 +15,33 @@ use std::num::*;
1415
///
1516
/// This trait is unsafe since &self might represent an invalid value. The `is_valid()` function
1617
/// must return `true` if and only if the invariant of its type is held.
18+
///
19+
/// # Deprecation
20+
///
21+
/// We have decided to deprecate this trait in favor of using `Arbitrary` for now. The main
22+
/// benefit of using Invariant was to avoid calling `kani::any_raw()` followed by `kani::assume()`.
23+
/// However, `kani::any_raw()` today cannot guarantee the rust type validity invariants, which
24+
/// could lead to UB in our analysis.
25+
#[deprecated(
26+
since = "0.8.0",
27+
note = "With `kani::Invariant`, Kani cannot guarantee that the type respects the language \
28+
type invariants which may trigger UB. Use `kani::Arbitrary` instead."
29+
)]
1730
pub unsafe trait Invariant {
1831
/// Check if `&self` holds a valid value that respect the type invariant.
1932
/// This function must return `true` if and only if `&self` is valid.
2033
fn is_valid(&self) -> bool;
2134
}
2235

23-
macro_rules! empty_invariant {
24-
( $type: ty ) => {
25-
unsafe impl Invariant for $type {
26-
#[inline(always)]
27-
fn is_valid(&self) -> bool {
28-
true
29-
}
30-
}
31-
};
32-
}
33-
34-
empty_invariant!(u8);
35-
empty_invariant!(u16);
36-
empty_invariant!(u32);
37-
empty_invariant!(u64);
38-
empty_invariant!(u128);
39-
empty_invariant!(usize);
40-
41-
empty_invariant!(i8);
42-
empty_invariant!(i16);
43-
empty_invariant!(i32);
44-
empty_invariant!(i64);
45-
empty_invariant!(i128);
46-
empty_invariant!(isize);
47-
48-
// We do not constraint floating points values per type spec. Users must add assumptions to their
49-
// verification code if they want to eliminate NaN, infinite, or subnormal.
50-
empty_invariant!(f32);
51-
empty_invariant!(f64);
52-
53-
empty_invariant!(());
54-
55-
unsafe impl Invariant for bool {
56-
#[inline(always)]
57-
fn is_valid(&self) -> bool {
58-
let byte = u8::from(*self);
59-
byte == 0 || byte == 1
60-
}
61-
}
62-
63-
/// Validate that a char is not outside the ranges [0x0, 0xD7FF] and [0xE000, 0x10FFFF]
64-
/// Ref: <https://doc.rust-lang.org/stable/nomicon/what-unsafe-does.html>
65-
unsafe impl Invariant for char {
66-
#[inline(always)]
67-
fn is_valid(&self) -> bool {
68-
// Kani translates char into i32.
69-
let val = *self as i32;
70-
val <= 0xD7FF || (0xE000..=0x10FFFF).contains(&val)
36+
// We cannot apply #[deprecated] to trait impl so add this to ignore the deprecation warnings.
37+
#[allow(deprecated)]
38+
impl<T> Arbitrary for T
39+
where
40+
T: Invariant,
41+
{
42+
default fn any() -> Self {
43+
let value = unsafe { crate::any_raw_internal::<T>() };
44+
crate::assume(value.is_valid());
45+
value
7146
}
7247
}
73-
74-
macro_rules! nonzero_invariant {
75-
( $type: ty ) => {
76-
unsafe impl Invariant for $type {
77-
#[inline(always)]
78-
fn is_valid(&self) -> bool {
79-
self.get() != 0
80-
}
81-
}
82-
};
83-
}
84-
85-
nonzero_invariant!(NonZeroU8);
86-
nonzero_invariant!(NonZeroU16);
87-
nonzero_invariant!(NonZeroU32);
88-
nonzero_invariant!(NonZeroU64);
89-
nonzero_invariant!(NonZeroU128);
90-
nonzero_invariant!(NonZeroUsize);
91-
92-
nonzero_invariant!(NonZeroI8);
93-
nonzero_invariant!(NonZeroI16);
94-
nonzero_invariant!(NonZeroI32);
95-
nonzero_invariant!(NonZeroI64);
96-
nonzero_invariant!(NonZeroI128);
97-
nonzero_invariant!(NonZeroIsize);

library/kani/src/lib.rs

+18-22
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod vec;
1111

1212
pub use arbitrary::Arbitrary;
1313
pub use futures::block_on;
14+
#[allow(deprecated)]
1415
pub use invariant::Invariant;
1516

1617
/// Creates an assumption that will be valid after this statement run. Note that the assumption
@@ -76,38 +77,32 @@ pub fn any<T: Arbitrary>() -> T {
7677

7778
/// This function creates an unconstrained value of type `T`. This may result in an invalid value.
7879
///
79-
/// # Example:
80-
///
81-
/// In the snippet below, we are verifying the behavior of the function `fn_under_verification`
82-
/// under all possible values of char, including invalid ones that are greater than char::MAX.
83-
///
84-
/// ```rust
85-
/// let inputA = unsafe { kani::any_raw::<char>() };
86-
/// fn_under_verification(inputA);
87-
/// ```
88-
///
8980
/// # Safety
9081
///
9182
/// This function is unsafe and it may represent invalid `T` values which can lead to many
92-
/// undesirable undefined behaviors. Users must validate that the symbolic variable respects
93-
/// the type invariant as well as any other constraint relevant to their usage. E.g.:
83+
/// undesirable undefined behaviors. Users must guarantee that an unconstrained symbolic value
84+
/// for type T only represents valid values.
9485
///
95-
/// ```rust
96-
/// let c = unsafe { kani::any_raw::char() };
97-
/// kani::assume(char::from_u32(c as u32).is_ok());
98-
/// ```
86+
/// # Deprecation
9987
///
100-
#[rustc_diagnostic_item = "KaniAnyRaw"]
88+
/// We have decided to deprecate this function due to the fact that its result can be the source
89+
/// of undefined behavior.
10190
#[inline(never)]
91+
#[deprecated(
92+
since = "0.8.0",
93+
note = "This function may return symbolic values that don't respects the language type invariants."
94+
)]
95+
#[doc(hidden)]
10296
pub unsafe fn any_raw<T>() -> T {
103-
unimplemented!("Kani any_raw")
97+
any_raw_internal()
10498
}
10599

106-
/// This function has been split into a safe and unsafe functions: `kani::any` and `kani::any_raw`.
107-
#[deprecated]
100+
/// This function will replace `any_raw` that has been deprecated and it should only be used
101+
/// internally when we can guarantee that it will not trigger any undefined behavior.
102+
#[rustc_diagnostic_item = "KaniAnyRaw"]
108103
#[inline(never)]
109-
pub fn nondet<T: Arbitrary>() -> T {
110-
any::<T>()
104+
pub(crate) unsafe fn any_raw_internal<T>() -> T {
105+
unimplemented!("Kani any_raw")
111106
}
112107

113108
/// Function used in tests for cases where the condition is not always true.
@@ -123,6 +118,7 @@ pub fn expect_fail(_cond: bool, _message: &'static str) {}
123118
/// overrides, but not the other way around.
124119
#[inline(never)]
125120
#[rustc_diagnostic_item = "KaniPanic"]
121+
#[doc(hidden)]
126122
pub fn panic(message: &'static str) -> ! {
127123
panic!("{}", message)
128124
}

library/kani/src/slice.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright Kani Contributors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
3-
use crate::{any, any_raw, assume, Arbitrary};
3+
use crate::{any, any_raw_internal, assume, Arbitrary};
44
use std::alloc::{alloc, dealloc, Layout};
55
use std::ops::{Deref, DerefMut};
66

@@ -39,8 +39,7 @@ fn any_range<const LENGTH: usize>() -> (usize, usize) {
3939
/// between `0..=MAX_SLICE_LENGTH` and with non-deterministic content. This is
4040
/// useful in situations where one wants to verify that all slices with any
4141
/// content and with a length up to `MAX_SLICE_LENGTH` satisfy a certain
42-
/// property. Use `any_slice` or `any_raw_slice` to create an instance of this
43-
/// struct.
42+
/// property. Use `any_slice` to create an instance of this struct.
4443
///
4544
/// # Example:
4645
///
@@ -79,13 +78,17 @@ impl<T, const MAX_SLICE_LENGTH: usize> AnySlice<T, MAX_SLICE_LENGTH> {
7978
any_slice
8079
}
8180

81+
#[deprecated(
82+
since = "0.8.0",
83+
note = "This function may return symbolic values that don't respects the language type invariants."
84+
)]
8285
fn new_raw() -> Self {
8386
let any_slice = AnySlice::<T, MAX_SLICE_LENGTH>::alloc_slice();
8487
unsafe {
8588
let mut i = 0;
8689
// See note on `MAX_SLICE_LENGTH` in `new` method above
8790
while i < any_slice.slice_len && i < MAX_SLICE_LENGTH {
88-
*any_slice.ptr.add(i) = any_raw();
91+
*any_slice.ptr.add(i) = any_raw_internal();
8992
i += 1;
9093
}
9194
}
@@ -151,8 +154,18 @@ where
151154

152155
/// # Safety
153156
///
154-
/// TODO: Safety of any_raw_slice is a complex matter. See
155-
/// https://github.com/model-checking/kani/issues/1394
157+
/// Users must guarantee that an unconstrained symbolic value for type T only represents valid
158+
/// values.
159+
///
160+
/// # Deprecated
161+
///
162+
/// This has been deprecated due to its limited value and high risk of generating undefined
163+
/// behavior.
164+
#[allow(deprecated)]
165+
#[deprecated(
166+
since = "0.8.0",
167+
note = "This function may return symbolic values that don't respects the language type invariants."
168+
)]
156169
pub unsafe fn any_raw_slice<T, const MAX_SLICE_LENGTH: usize>() -> AnySlice<T, MAX_SLICE_LENGTH> {
157170
AnySlice::<T, MAX_SLICE_LENGTH>::new_raw()
158171
}

0 commit comments

Comments
 (0)