Skip to content

Commit d4aa09f

Browse files
committed
Add a few intrinsics contracts
- This is not working due to a Kani limitation
1 parent df109da commit d4aa09f

File tree

3 files changed

+241
-3
lines changed

3 files changed

+241
-3
lines changed

doc/src/challenges/0002-intrinsics-memory.md

-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ Intrinsic functions to be annotated with safety contracts
3232
|copy| core::intrisics |
3333
|write_bytes| core::intrisics |
3434
|size_of_val| core::intrisics |
35-
|arith_offset| core::intrisics |
3635
|volatile_copy_nonoverlapping_memory| core::intrisics |
3736
|volatile_copy_memory| core::intrisics |
3837
|volatile_set_memory| core::intrisics |

library/core/src/intrinsics.rs

+175-2
Original file line numberDiff line numberDiff line change
@@ -1064,7 +1064,17 @@ extern "rust-intrinsic" {
10641064
/// The stabilized version of this intrinsic is [`crate::mem::size_of_val`].
10651065
#[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")]
10661066
#[rustc_nounwind]
1067-
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
1067+
// FIXME: Kani currently does not support annotating intrinsics.
1068+
// See https://github.com/model-checking/kani/issues/3325
1069+
#[cfg_attr(not(kani), requires(matches!(
1070+
<T as Pointee>::Metadata::map_dyn(crate::ptr::metadata(_val)::metadata(),
1071+
|dyn_meta| { ub_checks::can_dereference(dyn_meta)}),
1072+
None | Some(true))))]
1073+
#[cfg_attr(not(kani), requires(matches!(
1074+
<T as Pointee>::Metadata::map_len(crate::ptr::metadata(_val)::metadata(),
1075+
|_| { ub_checks::can_dereference(_val)}),
1076+
None | Some(true))))]
1077+
pub fn size_of_val<T: ?Sized>(_val: *const T) -> usize;
10681078
/// The required alignment of the referenced value.
10691079
///
10701080
/// The stabilized version of this intrinsic is [`core::mem::align_of_val`].
@@ -2790,6 +2800,9 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
27902800
#[unstable(feature = "core_intrinsics", issue = "none")]
27912801
#[rustc_intrinsic]
27922802
#[rustc_intrinsic_must_be_overridden]
2803+
// FIXME(kani): Cannot verify intrinsics contract yet.
2804+
// <https://github.com/model-checking/kani/issues/3345>
2805+
#[requires(ub_checks::can_dereference(_ptr as *const crate::ptr::DynMetadata<dyn crate::any::Any>))]
27932806
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
27942807
unreachable!()
27952808
}
@@ -2800,6 +2813,9 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
28002813
#[unstable(feature = "core_intrinsics", issue = "none")]
28012814
#[rustc_intrinsic]
28022815
#[rustc_intrinsic_must_be_overridden]
2816+
// FIXME(kani): Cannot verify intrinsics contract yet.
2817+
// <https://github.com/model-checking/kani/issues/3345>
2818+
#[requires(ub_checks::can_dereference(_ptr as *const crate::ptr::DynMetadata<dyn crate::any::Any>))]
28032819
pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
28042820
unreachable!()
28052821
}
@@ -2927,6 +2943,14 @@ impl<P: ?Sized, T: ptr::Thin> AggregateRawPtr<*mut T> for *mut P {
29272943
#[inline(always)]
29282944
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
29292945
#[rustc_diagnostic_item = "ptr_copy_nonoverlapping"]
2946+
#[requires(!count.overflowing_mul(size_of::<T>()).1
2947+
&& ub_checks::can_dereference(core::ptr::slice_from_raw_parts(src, count))
2948+
&& ub_checks::can_write(core::ptr::slice_from_raw_parts_mut(dst, count)))]
2949+
#[requires(src.addr() != dst.addr() || core::mem::size_of::<T>() == 0)]
2950+
#[requires((src.addr() >= dst.addr() + core::mem::size_of::<T>()) || (dst.addr() >= src.addr() + core::mem::size_of::<T>()))]
2951+
// TODO: Modifies doesn't work with slices today.
2952+
// https://github.com/model-checking/kani/pull/3295
2953+
// #[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))]
29302954
pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize) {
29312955
extern "rust-intrinsic" {
29322956
#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")]
@@ -3029,6 +3053,13 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
30293053
#[inline(always)]
30303054
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
30313055
#[rustc_diagnostic_item = "ptr_copy"]
3056+
// FIXME: How to verify safety for types that do not implement Copy and count > 1??
3057+
#[requires(!count.overflowing_mul(size_of::<T>()).1
3058+
&& ub_checks::can_dereference(core::ptr::slice_from_raw_parts(src, count))
3059+
&& ub_checks::can_write(core::ptr::slice_from_raw_parts_mut(dst, count)))]
3060+
// TODO: Modifies doesn't work with slices today.
3061+
// https://github.com/model-checking/kani/pull/3295
3062+
// #[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))]
30323063
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
30333064
extern "rust-intrinsic" {
30343065
#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")]
@@ -3048,7 +3079,10 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
30483079
align: usize = align_of::<T>(),
30493080
) =>
30503081
ub_checks::is_aligned_and_not_null(src, align)
3051-
&& ub_checks::is_aligned_and_not_null(dst, align)
3082+
3083+
3084+
3085+
&& ub_checks::is_aligned_and_not_null(dst, align)
30523086
);
30533087
copy(src, dst, count)
30543088
}
@@ -3110,6 +3144,11 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
31103144
#[inline(always)]
31113145
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
31123146
#[rustc_diagnostic_item = "ptr_write_bytes"]
3147+
#[requires(!count.overflowing_mul(size_of::<T>()).1
3148+
&& ub_checks::can_write(core::ptr::slice_from_raw_parts_mut(dst as *mut u8, count)))]
3149+
// TODO: Modifies doesn't work with slices today.
3150+
// https://github.com/model-checking/kani/pull/3295
3151+
// #[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))]
31133152
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
31143153
extern "rust-intrinsic" {
31153154
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
@@ -3185,4 +3224,138 @@ mod verify {
31853224
assert_eq!(y, old_x);
31863225
assert_eq!(x, old_y);
31873226
}
3227+
3228+
/// Note that in `core` we cannot check allocated pointers.
3229+
#[derive(Copy, Clone, Debug, PartialEq, Eq, kani::Arbitrary)]
3230+
enum PtrKind {
3231+
/// Dangling pointers
3232+
Dangling,
3233+
/// Null pointers
3234+
Null,
3235+
/// Stack pointers
3236+
Stack,
3237+
/// Overlapping pointers
3238+
Overlap,
3239+
/// Unaligned pointers
3240+
Unaligned,
3241+
/// Pointer to uninitialized memory
3242+
Uninitialized,
3243+
}
3244+
3245+
struct ArbitraryPointers<T> {
3246+
src: *const T,
3247+
dst: *mut T,
3248+
dst_kind: PtrKind,
3249+
src_kind: PtrKind,
3250+
}
3251+
3252+
impl<T: kani::Arbitrary> ArbitraryPointers<T> {
3253+
fn with_arbitrary<F>(harness: F) where F: FnOnce(ArbitraryPointers<u32>) {
3254+
#[repr(C)]
3255+
struct WithPadding {
3256+
byte: u8,
3257+
// padding in the middle.
3258+
bytes: u64,
3259+
}
3260+
let mut single = kani::any::<u32>();
3261+
let ptr1 = crate::ptr::addr_of_mut!(single);
3262+
3263+
let mut array = [kani::any::<u32>(); 100];
3264+
let ptr2 = crate::ptr::addr_of_mut!(array) as *mut u32;
3265+
3266+
let mut buffer = [0u8, 6];
3267+
let unaligned = unsafe { crate::ptr::addr_of_mut!(buffer).byte_offset(1) } as *mut u32;
3268+
3269+
let mut padding = WithPadding { byte: 0, bytes: 0};
3270+
let uninit = unsafe { crate::ptr::addr_of_mut!(padding.byte).byte_offset(4)} as *mut u32;
3271+
3272+
let arbitrary = ArbitraryPointers::from(ptr1, ptr2, unaligned, uninit);
3273+
harness(arbitrary);
3274+
}
3275+
3276+
fn from(ptr1: *mut T, ptr2: *mut T, unaligned: *mut T, uninit: *mut T) -> Self {
3277+
let (src_kind, src) = ArbitraryPointers::any(ptr1, ptr2, unaligned, uninit);
3278+
let (dst_kind, dst) = ArbitraryPointers::any(ptr2, ptr1, unaligned, uninit);
3279+
ArbitraryPointers {
3280+
src_kind,
3281+
src,
3282+
dst_kind,
3283+
dst,
3284+
}
3285+
}
3286+
3287+
fn any(unique: *mut T, overlap: *mut T, unaligned: *mut T, uninit: *mut T) -> (PtrKind, *mut T) {
3288+
let kind = kani::any::<PtrKind>();
3289+
let ptr = match kind {
3290+
PtrKind::Dangling => {
3291+
crate::ptr::dangling_mut::<T>()
3292+
}
3293+
PtrKind::Null => {
3294+
crate::ptr::null_mut::<T>()
3295+
}
3296+
PtrKind::Stack => {
3297+
unique
3298+
}
3299+
PtrKind::Overlap => {
3300+
if kani::any() { unique } else { overlap }
3301+
}
3302+
PtrKind::Unaligned => {
3303+
unaligned
3304+
}
3305+
PtrKind::Uninitialized => {
3306+
uninit
3307+
}
3308+
};
3309+
(kind, ptr)
3310+
}
3311+
}
3312+
3313+
/// This harness currently fails because we cannot define the modifies clause for slices.
3314+
#[kani::proof_for_contract(copy)]
3315+
fn check_copy() {
3316+
ArbitraryPointers::<u32>::with_arbitrary(|arbitrary| {
3317+
// Skip dangling for now since it makes Kani contract panic.
3318+
// Verify this case in a separate harness.
3319+
kani::assume(arbitrary.dst_kind != PtrKind::Dangling);
3320+
kani::assume(arbitrary.src_kind != PtrKind::Dangling);
3321+
unsafe { copy(arbitrary.src, arbitrary.dst, kani::any()) }
3322+
});
3323+
}
3324+
3325+
/// This harness fails because Kani cannot assume a pointer is valid.
3326+
///
3327+
/// The specification should ensure the pointers passed are allocated, and this harness should
3328+
/// succeed since `copy` ensures would reject dangling pointers.
3329+
#[cfg(ignore)]
3330+
#[kani::proof_for_contract(copy)]
3331+
fn check_copy_dangling() {
3332+
ArbitraryPointers::<u32>::with_arbitrary(|arbitrary| {
3333+
// Verify the dangling case.
3334+
kani::assume(arbitrary.dst_kind == PtrKind::Dangling || arbitrary.src_kind == PtrKind::Dangling);
3335+
unsafe { copy(arbitrary.src, arbitrary.dst, kani::any()) }
3336+
});
3337+
}
3338+
3339+
/// This harness currently fails because we cannot define the modifies clause for slices.
3340+
#[kani::proof_for_contract(copy_nonoverlapping)]
3341+
fn check_copy_nonoverlapping() {
3342+
ArbitraryPointers::<u32>::with_arbitrary(|arbitrary| {
3343+
// Skip dangling for now since it makes Kani contract panic.
3344+
// Verify this case in a separate harness.
3345+
kani::assume(arbitrary.dst_kind != PtrKind::Dangling);
3346+
kani::assume(arbitrary.src_kind != PtrKind::Dangling);
3347+
unsafe { copy_nonoverlapping(arbitrary.src, arbitrary.dst, kani::any()) }
3348+
});
3349+
}
3350+
3351+
/// This harness currently fails because we cannot define the modifies clause for slices.
3352+
#[kani::proof_for_contract(write_bytes)]
3353+
fn check_write_bytes() {
3354+
ArbitraryPointers::<u32>::with_arbitrary(|arbitrary| {
3355+
// Skip dangling for now since it makes Kani contract panic.
3356+
// Verify this case in a separate harness.
3357+
kani::assume(arbitrary.dst_kind != PtrKind::Dangling);
3358+
unsafe { write_bytes(arbitrary.dst, kani::any(), kani::any()) }
3359+
});
3360+
}
31883361
}

library/core/src/ub_checks.rs

+66
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,72 @@ pub(crate) const fn is_nonoverlapping(
163163

164164
pub use predicates::*;
165165

166+
pub(crate) trait MetadataPredicates<T> where T: ?Sized {
167+
/// If the metadata is the length of a slice or str, run the map function.
168+
fn map_len<U, F>(metadata: *const Self, map: F) -> Option<U>
169+
where
170+
F: Fn(*const usize) -> U;
171+
172+
/// If the metadata is of type DynMetadata, run the map function and return its result.
173+
fn map_dyn<U, F>(metadata: *const Self, map: F) -> Option<U>
174+
where
175+
F: Fn(*const crate::ptr::DynMetadata<T>) -> U;
176+
}
177+
178+
impl<T> MetadataPredicates<T> for () {
179+
/// Return None.
180+
fn map_len<U, F>(_metadata: *const Self, _map: F) -> Option<U>
181+
where
182+
F: Fn(*const usize) -> U,
183+
{
184+
None
185+
}
186+
187+
/// Return None.
188+
fn map_dyn<U, F>(_metadata: *const Self, _map: F) -> Option<U>
189+
where
190+
F: Fn(*const crate::ptr::DynMetadata<T>) -> U,
191+
{
192+
None
193+
}
194+
}
195+
196+
impl<T> MetadataPredicates<T> for usize where T: ?Sized{
197+
/// Return the result of the map function.
198+
fn map_len<U, F>(metadata: *const Self, map: F) -> Option<U>
199+
where
200+
F: Fn(*const usize) -> U,
201+
{
202+
Some(map(metadata))
203+
}
204+
205+
/// This is not a DynMetadata. Return `None`.
206+
fn map_dyn<U, F>(_metadata: *const Self, _map: F) -> Option<U>
207+
where
208+
F: Fn(*const crate::ptr::DynMetadata<T>) -> U
209+
{
210+
None
211+
}
212+
}
213+
214+
impl<T> MetadataPredicates<T> for crate::ptr::DynMetadata<T> where T: ?Sized {
215+
/// Not a length. Return None.
216+
fn map_len<U, F>(_metadata: *const Self, _map: F) -> Option<U>
217+
where
218+
F: Fn(*const usize) -> U,
219+
{
220+
None
221+
}
222+
223+
/// Return the result of the map function.
224+
fn map_dyn<U, F>(metadata: *const Self, map: F) -> Option<U>
225+
where
226+
F: Fn(*const crate::ptr::DynMetadata<T>) -> U,
227+
{
228+
Some(map(metadata))
229+
}
230+
}
231+
166232
/// Provide a few predicates to be used in safety contracts.
167233
///
168234
/// At runtime, they are no-op, and always return true.

0 commit comments

Comments
 (0)