Skip to content

Commit aa1b9c2

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

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
@@ -1063,7 +1063,17 @@ extern "rust-intrinsic" {
10631063
/// The stabilized version of this intrinsic is [`crate::mem::size_of_val`].
10641064
#[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")]
10651065
#[rustc_nounwind]
1066-
pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
1066+
// FIXME: Kani currently does not support annotating intrinsics.
1067+
// See https://github.com/model-checking/kani/issues/3325
1068+
#[cfg_attr(not(kani), requires(matches!(
1069+
<T as Pointee>::Metadata::map_dyn(crate::ptr::metadata(_val)::metadata(),
1070+
|dyn_meta| { ub_checks::can_dereference(dyn_meta)}),
1071+
None | Some(true))))]
1072+
#[cfg_attr(not(kani), requires(matches!(
1073+
<T as Pointee>::Metadata::map_len(crate::ptr::metadata(_val)::metadata(),
1074+
|_| { ub_checks::can_dereference(_val)}),
1075+
None | Some(true))))]
1076+
pub fn size_of_val<T: ?Sized>(_val: *const T) -> usize;
10671077
/// The required alignment of the referenced value.
10681078
///
10691079
/// The stabilized version of this intrinsic is [`core::mem::align_of_val`].
@@ -2789,6 +2799,9 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
27892799
#[unstable(feature = "core_intrinsics", issue = "none")]
27902800
#[rustc_intrinsic]
27912801
#[rustc_intrinsic_must_be_overridden]
2802+
// FIXME(kani): Cannot verify intrinsics contract yet.
2803+
// <https://github.com/model-checking/kani/issues/3345>
2804+
#[requires(ub_checks::can_dereference(_ptr as *const crate::ptr::DynMetadata<dyn crate::any::Any>))]
27922805
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
27932806
unreachable!()
27942807
}
@@ -2799,6 +2812,9 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
27992812
#[unstable(feature = "core_intrinsics", issue = "none")]
28002813
#[rustc_intrinsic]
28012814
#[rustc_intrinsic_must_be_overridden]
2815+
// FIXME(kani): Cannot verify intrinsics contract yet.
2816+
// <https://github.com/model-checking/kani/issues/3345>
2817+
#[requires(ub_checks::can_dereference(_ptr as *const crate::ptr::DynMetadata<dyn crate::any::Any>))]
28022818
pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
28032819
unreachable!()
28042820
}
@@ -2940,6 +2956,14 @@ pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *cons
29402956
#[inline(always)]
29412957
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
29422958
#[rustc_diagnostic_item = "ptr_copy_nonoverlapping"]
2959+
#[requires(!count.overflowing_mul(size_of::<T>()).1
2960+
&& ub_checks::can_dereference(core::ptr::slice_from_raw_parts(src, count))
2961+
&& ub_checks::can_write(core::ptr::slice_from_raw_parts_mut(dst, count)))]
2962+
#[requires(src.addr() != dst.addr() || core::mem::size_of::<T>() == 0)]
2963+
#[requires((src.addr() >= dst.addr() + core::mem::size_of::<T>()) || (dst.addr() >= src.addr() + core::mem::size_of::<T>()))]
2964+
// TODO: Modifies doesn't work with slices today.
2965+
// https://github.com/model-checking/kani/pull/3295
2966+
// #[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))]
29432967
pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize) {
29442968
extern "rust-intrinsic" {
29452969
#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")]
@@ -3042,6 +3066,13 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
30423066
#[inline(always)]
30433067
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
30443068
#[rustc_diagnostic_item = "ptr_copy"]
3069+
// FIXME: How to verify safety for types that do not implement Copy and count > 1??
3070+
#[requires(!count.overflowing_mul(size_of::<T>()).1
3071+
&& ub_checks::can_dereference(core::ptr::slice_from_raw_parts(src, count))
3072+
&& ub_checks::can_write(core::ptr::slice_from_raw_parts_mut(dst, count)))]
3073+
// TODO: Modifies doesn't work with slices today.
3074+
// https://github.com/model-checking/kani/pull/3295
3075+
// #[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))]
30453076
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
30463077
extern "rust-intrinsic" {
30473078
#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")]
@@ -3060,7 +3091,10 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
30603091
align: usize = align_of::<T>(),
30613092
) =>
30623093
ub_checks::is_aligned_and_not_null(src, align)
3063-
&& ub_checks::is_aligned_and_not_null(dst, align)
3094+
3095+
3096+
3097+
&& ub_checks::is_aligned_and_not_null(dst, align)
30643098
);
30653099
copy(src, dst, count)
30663100
}
@@ -3122,6 +3156,11 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
31223156
#[inline(always)]
31233157
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
31243158
#[rustc_diagnostic_item = "ptr_write_bytes"]
3159+
#[requires(!count.overflowing_mul(size_of::<T>()).1
3160+
&& ub_checks::can_write(core::ptr::slice_from_raw_parts_mut(dst as *mut u8, count)))]
3161+
// TODO: Modifies doesn't work with slices today.
3162+
// https://github.com/model-checking/kani/pull/3295
3163+
// #[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))]
31253164
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
31263165
extern "rust-intrinsic" {
31273166
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
@@ -3197,4 +3236,138 @@ mod verify {
31973236
assert_eq!(y, old_x);
31983237
assert_eq!(x, old_y);
31993238
}
3239+
3240+
/// Note that in `core` we cannot check allocated pointers.
3241+
#[derive(Copy, Clone, Debug, PartialEq, Eq, kani::Arbitrary)]
3242+
enum PtrKind {
3243+
/// Dangling pointers
3244+
Dangling,
3245+
/// Null pointers
3246+
Null,
3247+
/// Stack pointers
3248+
Stack,
3249+
/// Overlapping pointers
3250+
Overlap,
3251+
/// Unaligned pointers
3252+
Unaligned,
3253+
/// Pointer to uninitialized memory
3254+
Uninitialized,
3255+
}
3256+
3257+
struct ArbitraryPointers<T> {
3258+
src: *const T,
3259+
dst: *mut T,
3260+
dst_kind: PtrKind,
3261+
src_kind: PtrKind,
3262+
}
3263+
3264+
impl<T: kani::Arbitrary> ArbitraryPointers<T> {
3265+
fn with_arbitrary<F>(harness: F) where F: FnOnce(ArbitraryPointers<u32>) {
3266+
#[repr(C)]
3267+
struct WithPadding {
3268+
byte: u8,
3269+
// padding in the middle.
3270+
bytes: u64,
3271+
}
3272+
let mut single = kani::any::<u32>();
3273+
let ptr1 = crate::ptr::addr_of_mut!(single);
3274+
3275+
let mut array = [kani::any::<u32>(); 100];
3276+
let ptr2 = crate::ptr::addr_of_mut!(array) as *mut u32;
3277+
3278+
let mut buffer = [0u8, 6];
3279+
let unaligned = unsafe { crate::ptr::addr_of_mut!(buffer).byte_offset(1) } as *mut u32;
3280+
3281+
let mut padding = WithPadding { byte: 0, bytes: 0};
3282+
let uninit = unsafe { crate::ptr::addr_of_mut!(padding.byte).byte_offset(4)} as *mut u32;
3283+
3284+
let arbitrary = ArbitraryPointers::from(ptr1, ptr2, unaligned, uninit);
3285+
harness(arbitrary);
3286+
}
3287+
3288+
fn from(ptr1: *mut T, ptr2: *mut T, unaligned: *mut T, uninit: *mut T) -> Self {
3289+
let (src_kind, src) = ArbitraryPointers::any(ptr1, ptr2, unaligned, uninit);
3290+
let (dst_kind, dst) = ArbitraryPointers::any(ptr2, ptr1, unaligned, uninit);
3291+
ArbitraryPointers {
3292+
src_kind,
3293+
src,
3294+
dst_kind,
3295+
dst,
3296+
}
3297+
}
3298+
3299+
fn any(unique: *mut T, overlap: *mut T, unaligned: *mut T, uninit: *mut T) -> (PtrKind, *mut T) {
3300+
let kind = kani::any::<PtrKind>();
3301+
let ptr = match kind {
3302+
PtrKind::Dangling => {
3303+
crate::ptr::dangling_mut::<T>()
3304+
}
3305+
PtrKind::Null => {
3306+
crate::ptr::null_mut::<T>()
3307+
}
3308+
PtrKind::Stack => {
3309+
unique
3310+
}
3311+
PtrKind::Overlap => {
3312+
if kani::any() { unique } else { overlap }
3313+
}
3314+
PtrKind::Unaligned => {
3315+
unaligned
3316+
}
3317+
PtrKind::Uninitialized => {
3318+
uninit
3319+
}
3320+
};
3321+
(kind, ptr)
3322+
}
3323+
}
3324+
3325+
/// This harness currently fails because we cannot define the modifies clause for slices.
3326+
#[kani::proof_for_contract(copy)]
3327+
fn check_copy() {
3328+
ArbitraryPointers::<u32>::with_arbitrary(|arbitrary| {
3329+
// Skip dangling for now since it makes Kani contract panic.
3330+
// Verify this case in a separate harness.
3331+
kani::assume(arbitrary.dst_kind != PtrKind::Dangling);
3332+
kani::assume(arbitrary.src_kind != PtrKind::Dangling);
3333+
unsafe { copy(arbitrary.src, arbitrary.dst, kani::any()) }
3334+
});
3335+
}
3336+
3337+
/// This harness fails because Kani cannot assume a pointer is valid.
3338+
///
3339+
/// The specification should ensure the pointers passed are allocated, and this harness should
3340+
/// succeed since `copy` ensures would reject dangling pointers.
3341+
#[cfg(ignore)]
3342+
#[kani::proof_for_contract(copy)]
3343+
fn check_copy_dangling() {
3344+
ArbitraryPointers::<u32>::with_arbitrary(|arbitrary| {
3345+
// Verify the dangling case.
3346+
kani::assume(arbitrary.dst_kind == PtrKind::Dangling || arbitrary.src_kind == PtrKind::Dangling);
3347+
unsafe { copy(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(copy_nonoverlapping)]
3353+
fn check_copy_nonoverlapping() {
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+
kani::assume(arbitrary.src_kind != PtrKind::Dangling);
3359+
unsafe { copy_nonoverlapping(arbitrary.src, arbitrary.dst, kani::any()) }
3360+
});
3361+
}
3362+
3363+
/// This harness currently fails because we cannot define the modifies clause for slices.
3364+
#[kani::proof_for_contract(write_bytes)]
3365+
fn check_write_bytes() {
3366+
ArbitraryPointers::<u32>::with_arbitrary(|arbitrary| {
3367+
// Skip dangling for now since it makes Kani contract panic.
3368+
// Verify this case in a separate harness.
3369+
kani::assume(arbitrary.dst_kind != PtrKind::Dangling);
3370+
unsafe { write_bytes(arbitrary.dst, kani::any(), kani::any()) }
3371+
});
3372+
}
32003373
}

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)