Skip to content

Commit a48ef98

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

File tree

2 files changed

+235
-1
lines changed

2 files changed

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

0 commit comments

Comments
 (0)