Skip to content

Commit 7a5265e

Browse files
committed
[kani] Prove some PtrInner methods
gherrit-pr-id: Ic1e352d3d92a45ff86d8ca85b8221bccfb07033b
1 parent 2bfd906 commit 7a5265e

File tree

2 files changed

+237
-1
lines changed

2 files changed

+237
-1
lines changed

src/lib.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ use std::io;
377377

378378
use crate::pointer::invariant::{self, BecauseExclusive};
379379

380-
#[cfg(any(feature = "alloc", test))]
380+
#[cfg(any(feature = "alloc", test, kani))]
381381
extern crate alloc;
382382
#[cfg(any(feature = "alloc", test))]
383383
use alloc::{boxed::Box, vec::Vec};
@@ -738,8 +738,13 @@ pub unsafe trait KnownLayout {
738738
/// The type of metadata stored in a pointer to `Self`.
739739
///
740740
/// This is `()` for sized types and `usize` for slice DSTs.
741+
#[cfg(not(kani))]
741742
type PointerMetadata: PointerMetadata;
742743

744+
#[cfg(kani)]
745+
#[allow(missing_docs)]
746+
type PointerMetadata: PointerMetadata + kani::Arbitrary;
747+
743748
/// A maybe-uninitialized analog of `Self`
744749
///
745750
/// # Safety

src/pointer/inner.rs

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,3 +519,234 @@ mod tests {
519519
}
520520
}
521521
}
522+
523+
#[cfg(kani)]
524+
mod proofs {
525+
use core::alloc::Layout;
526+
527+
use crate::{SizeInfo, TrailingSliceLayout};
528+
529+
use super::*;
530+
531+
impl<T> kani::Arbitrary for PtrInner<'static, T>
532+
where
533+
T: ?Sized + KnownLayout,
534+
{
535+
fn any() -> Self {
536+
let (size, meta) = match T::LAYOUT.size_info {
537+
SizeInfo::Sized { size } => (size, T::PointerMetadata::from_elem_count(0)),
538+
SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => {
539+
let meta = T::PointerMetadata::from_elem_count(kani::any());
540+
let size = meta.size_for_metadata(T::LAYOUT);
541+
kani::assume(size.is_some());
542+
let size = size.unwrap();
543+
kani::assume(size <= isize::MAX as usize);
544+
545+
(size, meta)
546+
}
547+
};
548+
549+
let layout =
550+
alloc::alloc::Layout::from_size_align(size, T::LAYOUT.align.get()).unwrap();
551+
552+
let ptr = if layout.size() == 0 {
553+
NonNull::dangling()
554+
} else {
555+
let ptr = unsafe { alloc::alloc::alloc(layout) };
556+
NonNull::new(ptr).unwrap_or_else(|| alloc::alloc::handle_alloc_error(layout))
557+
};
558+
559+
let ptr = T::raw_from_ptr_len(ptr, meta);
560+
unsafe { PtrInner::new(ptr) }
561+
}
562+
}
563+
564+
impl<'a, T> PtrInner<'a, T>
565+
where
566+
T: ?Sized + KnownLayout,
567+
{
568+
fn assert_invariants(&self) {
569+
let ptr = self.as_non_null();
570+
let size = T::size_of_val_raw(ptr).unwrap();
571+
assert!(size <= isize::MAX as usize);
572+
573+
let addr = ptr.addr().get();
574+
// Assert that the referent doesn't wrap around the address space.
575+
assert!(addr.checked_add(size).is_some());
576+
}
577+
}
578+
579+
macro_rules! prove_foreach {
580+
($generic:ident { $($concrete:ident = $ty:ty,)* }) => {
581+
$(
582+
#[kani::proof]
583+
fn $concrete() {
584+
$generic::<$ty>();
585+
}
586+
)*
587+
};
588+
}
589+
590+
fn prove_arbitrary<T: 'static + ?Sized + KnownLayout>() {
591+
let ptr: PtrInner<'static, T> = kani::any();
592+
ptr.assert_invariants();
593+
}
594+
595+
// On 64-bit targets, Rust uses 2^61 - 1 rather than 2^63 - 1 as the maximum
596+
// object size [1].
597+
//
598+
// [1] https://github.com/rust-lang/rust/blob/493c38ba371929579fe136df26eccd9516347c7a/compiler/rustc_abi/src/lib.rs#L410
599+
const MAX_SIZE: usize = 1 << 61 - 1;
600+
601+
prove_foreach! {
602+
prove_arbitrary {
603+
prove_arbitrary_empty_tuple = (),
604+
prove_arbitrary_u8 = u8,
605+
prove_arbitrary_u16 = u16,
606+
prove_arbitrary_slice_u8 = [u8],
607+
prove_arbitrary_slice_u16 = [u16],
608+
prove_arbitrary_large_u8 = [u8; MAX_SIZE],
609+
prove_arbitrary_large_slice_u8 = [[u8; MAX_SIZE]],
610+
prove_arbitrary_large_u16 = [u16; (MAX_SIZE)/2],
611+
prove_arbitrary_large_slice_u16 = [[u16; (MAX_SIZE)/2]],
612+
}
613+
}
614+
615+
fn prove_slice_unchecked<T: 'static>() {
616+
let ptr: PtrInner<'static, [T]> = kani::any();
617+
let start = kani::any();
618+
let end = kani::any();
619+
620+
kani::assume(start <= end);
621+
kani::assume(end <= ptr.len());
622+
623+
let slc = unsafe { ptr.slice_unchecked(Range { start, end }) };
624+
slc.assert_invariants();
625+
626+
assert_eq!(slc.len(), end - start);
627+
628+
let begin = unsafe { slc.as_non_null().cast::<T>().sub(start) };
629+
assert_eq!(begin, ptr.as_non_null().cast::<T>());
630+
}
631+
632+
prove_foreach! {
633+
prove_slice_unchecked {
634+
prove_slice_unchecked_empty_tuple = (),
635+
prove_slice_unchecked_u8 = u8,
636+
prove_slice_unchecked_u16 = u16,
637+
prove_slice_unchecked_large_u8 = [u8; MAX_SIZE],
638+
prove_slice_unchecked_large_u16 = [u16; (MAX_SIZE)/2],
639+
}
640+
}
641+
642+
fn prove_split_at<T: 'static>() {
643+
let ptr: PtrInner<'static, [T]> = kani::any();
644+
let l_len = kani::any();
645+
646+
kani::assume(l_len <= ptr.len());
647+
648+
let (left, right) = unsafe { ptr.split_at(l_len) };
649+
left.assert_invariants();
650+
right.assert_invariants();
651+
652+
assert_eq!(left.len(), l_len);
653+
assert_eq!(left.len() + right.len(), ptr.len());
654+
assert_eq!(left.as_non_null().cast::<T>(), ptr.as_non_null().cast::<T>());
655+
}
656+
657+
prove_foreach! {
658+
prove_split_at {
659+
prove_split_at_empty_tuple = (),
660+
prove_split_at_u8 = u8,
661+
prove_split_at_u16 = u16,
662+
prove_split_at_large_u8 = [u8; MAX_SIZE],
663+
prove_split_at_large_u16 = [u16; (MAX_SIZE)/2],
664+
}
665+
}
666+
667+
#[kani::proof]
668+
fn prove_as_slice() {
669+
fn inner<T: 'static, const N: usize>() {
670+
let ptr: PtrInner<'static, [T; N]> = kani::any();
671+
let slc = ptr.as_slice();
672+
slc.assert_invariants();
673+
674+
assert_eq!(ptr.as_non_null().cast::<T>(), slc.as_non_null().cast::<T>());
675+
assert_eq!(slc.len(), N);
676+
}
677+
678+
inner::<(), 0>();
679+
inner::<(), 1>();
680+
inner::<(), MAX_SIZE>();
681+
682+
inner::<u8, 0>();
683+
inner::<u8, 1>();
684+
inner::<u8, MAX_SIZE>();
685+
686+
inner::<[u8; MAX_SIZE / 2], 0>();
687+
inner::<[u8; MAX_SIZE / 2], 1>();
688+
inner::<[u8; MAX_SIZE / 2], 2>();
689+
690+
inner::<[u8; MAX_SIZE], 0>();
691+
inner::<[u8; MAX_SIZE], 1>();
692+
}
693+
694+
impl kani::Arbitrary for CastType {
695+
fn any() -> CastType {
696+
if kani::any() {
697+
CastType::Prefix
698+
} else {
699+
CastType::Suffix
700+
}
701+
}
702+
}
703+
704+
fn prove_try_cast_into<T: 'static + ?Sized + KnownLayout>() {
705+
let ptr: PtrInner<'static, [u8]> = kani::any();
706+
let cast_type = kani::any();
707+
let meta = kani::any();
708+
709+
match ptr.try_cast_into::<T>(cast_type, meta) {
710+
Ok((t, rest)) => {
711+
t.assert_invariants();
712+
713+
let ptr_u8 = ptr.as_non_null().cast::<u8>();
714+
let t_u8 = t.as_non_null().cast::<u8>();
715+
let rest_u8 = rest.as_non_null().cast::<u8>();
716+
match cast_type {
717+
CastType::Prefix => {
718+
assert_eq!(t_u8, ptr_u8);
719+
// TODO: Assert that `t` and `rest` are contiguous and
720+
// non-overlapping.
721+
}
722+
CastType::Suffix => {
723+
assert_eq!(rest_u8, ptr_u8);
724+
// Asserts that `rest` and `t` are contiguous and
725+
// non-overlapping.
726+
assert_eq!(unsafe { rest_u8.add(rest.len()) }, t_u8);
727+
}
728+
}
729+
}
730+
Err(CastError::Alignment(_)) => {
731+
// TODO: Prove that there would have been an alignment problem.
732+
}
733+
Err(CastError::Size(_)) => {
734+
// TODO: Prove that there would have been a size problem.
735+
}
736+
}
737+
}
738+
739+
prove_foreach! {
740+
prove_try_cast_into {
741+
prove_try_cast_into_empty_tuple = (),
742+
prove_try_cast_into_u8 = u8,
743+
prove_try_cast_into_u16 = u16,
744+
prove_try_cast_into_slice_u8 = [u8],
745+
prove_try_cast_into_slice_u16 = [u16],
746+
prove_try_cast_into_large_u8 = [u8; MAX_SIZE],
747+
prove_try_cast_into_large_slice_u8 = [[u8; MAX_SIZE]],
748+
prove_try_cast_into_large_u16 = [u16; (MAX_SIZE)/2],
749+
prove_try_cast_into_large_slice_u16 = [[u16; (MAX_SIZE)/2]],
750+
}
751+
}
752+
}

0 commit comments

Comments
 (0)