creusot_contracts/ghost/
ptr_own.rs

1//! Raw pointers with ghost code
2
3use std::marker::PhantomData;
4
5use crate::prelude::*;
6#[cfg(creusot)]
7use crate::std::{
8    mem::{size_of_logic, size_of_val_logic},
9    ptr::{metadata_logic, metadata_matches},
10};
11
12/// Token that represents the ownership of a memory cell
13///
14/// A `PtrOwn` value only exists in the ghost world, but can be used
15/// in combination with a corresponding `*const` to access and modify memory.
16///
17/// A warning regarding memory leaks: dropping a `Ghost<PtrOwn<T>>` (we only
18/// ever handle ghost `PtrOwn` values) cannot deallocate the memory
19/// corresponding to the pointer because it is a ghost value. One must thus
20/// remember to explicitly call [`drop`] in order to free the memory tracked by a
21/// `PtrOwn` token.
22///
23/// # Safety
24///
25/// When using Creusot to verify the code, all methods should be safe to call. Indeed,
26/// Creusot ensures that every operation on the inner value uses the right [`PtrOwn`] object
27/// created by [`PtrOwn::new`], ensuring safety in a manner similar to [ghost_cell](https://docs.rs/ghost-cell/latest/ghost_cell/).
28///
29/// # `#[check(terminates)]`
30///
31/// `PtrOwn` methods, particularly constructors (`new`, `from_box`, `from_ref`,
32/// `from_mut`), are marked `check(terminates)` rather than `check(ghost)`
33/// to prevent two things from happening in ghost code:
34/// 1. running out of pointer addresses;
35/// 2. allocating too large objects.
36///
37/// Note that we already can't guard against these issues in program code.
38/// But preventing them in ghost code is even more imperative to ensure soundness.
39///
40/// Specifically, creating too many pointers contradicts the [`PtrOwn::disjoint_lemma`],
41/// and allocating too large objects contradicts the [`PtrOwn::invariant`] that
42/// allocations have size at most `isize::MAX`.
43#[allow(dead_code)]
44#[opaque]
45pub struct PtrOwn<T: ?Sized>(PhantomData<T>);
46
47impl<T: ?Sized> PtrOwn<T> {
48    /// The raw pointer whose ownership is tracked by this `PtrOwn`
49    #[logic(opaque)]
50    pub fn ptr(self) -> *const T {
51        dead
52    }
53
54    /// The value currently stored at address [`self.ptr()`](Self::ptr)
55    #[logic(opaque)]
56    pub fn val<'a>(self) -> &'a T {
57        dead
58    }
59}
60
61impl<T: ?Sized> Invariant for PtrOwn<T> {
62    #[logic(open, prophetic)]
63    fn invariant(self) -> bool {
64        pearlite! {
65            !self.ptr().is_null_logic()
66                && self.ptr_is_aligned_opaque()
67                && metadata_matches(*self.val(), metadata_logic(self.ptr()))
68                // Allocations can never be larger than `isize` (source: https://doc.rust-lang.org/std/ptr/index.html#allocation)
69                && size_of_val_logic(*self.val()) <= isize::MAX@
70                // The allocation fits in the address space
71                // (this is needed to verify (a `PtrOwn` variant of) `<*const T>::add`, which checks this condition)
72                && self.ptr().addr_logic()@ + size_of_val_logic(*self.val()) <= usize::MAX@
73                && inv(self.val())
74        }
75    }
76}
77
78impl<T> PtrOwn<T> {
79    /// Creates a new `PtrOwn` and associated `*const` by allocating a new memory
80    /// cell initialized with `v`.
81    #[check(terminates)] // can overflow the number of available pointer adresses
82    #[ensures(result.1.ptr() == result.0 && *result.1.val() == v)]
83    pub fn new(v: T) -> (*mut T, Ghost<PtrOwn<T>>) {
84        Self::from_box(Box::new(v))
85    }
86
87    /// If one owns two `PtrOwn`s for non-zero sized types, then they are for different pointers.
88    #[trusted]
89    #[check(ghost)]
90    #[requires(size_of_logic::<T>() != 0)]
91    #[ensures(own1.ptr().addr_logic() != own2.ptr().addr_logic())]
92    #[ensures(*own1 == ^own1)]
93    #[allow(unused_variables)]
94    pub fn disjoint_lemma(own1: &mut PtrOwn<T>, own2: &PtrOwn<T>) {}
95}
96
97impl<T: ?Sized> PtrOwn<T> {
98    /// Creates a ghost `PtrOwn` and associated `*const` from an existing [`Box`].
99    #[trusted]
100    #[check(terminates)] // can overflow the number of available pointer adresses
101    #[ensures(result.1.ptr() == result.0 && *result.1.val() == *val)]
102    #[erasure(Box::into_raw)]
103    pub fn from_box(val: Box<T>) -> (*mut T, Ghost<PtrOwn<T>>) {
104        (Box::into_raw(val), Ghost::conjure())
105    }
106
107    /// Decompose a shared reference into a raw pointer and a ghost `PtrOwn`.
108    ///
109    /// # Erasure
110    ///
111    /// This function erases to a raw reborrow of a reference.
112    ///
113    /// ```ignore
114    /// PtrOwn::from_ref(r)
115    /// // erases to
116    /// r as *const T  // or *mut T (both are allowed)
117    /// ```
118    #[trusted]
119    #[check(terminates)] // can overflow the number of available pointer adresses
120    #[ensures(result.1.ptr() == result.0)]
121    #[ensures(*result.1.val() == *r)]
122    #[intrinsic("ptr_own_from_ref")]
123    pub fn from_ref(r: &T) -> (*const T, Ghost<&PtrOwn<T>>) {
124        (r, Ghost::conjure())
125    }
126
127    /// Decompose a mutable reference into a raw pointer and a ghost `PtrOwn`.
128    ///
129    /// # Erasure
130    ///
131    /// This function erases to a raw reborrow of a reference.
132    ///
133    /// ```ignore
134    /// PtrOwn::from_mut(r)
135    /// // erases to
136    /// r as *const T  // or *mut T (both are allowed)
137    /// ```
138    #[trusted]
139    #[check(terminates)] // can overflow the number of available pointer adresses
140    #[ensures(result.1.ptr() == result.0)]
141    #[ensures(*result.1.val() == *r)]
142    #[ensures(*(^result.1.inner_logic()).val() == ^r)]
143    #[intrinsic("ptr_own_from_mut")]
144    pub fn from_mut(r: &mut T) -> (*mut T, Ghost<&mut PtrOwn<T>>) {
145        (r, Ghost::conjure())
146    }
147
148    /// Immutably borrows the underlying `T`.
149    ///
150    /// # Safety
151    ///
152    /// Safety requirements are the same as a direct dereference: `&*ptr`.
153    ///
154    /// Creusot will check that all calls to this function are indeed safe: see the
155    /// [type documentation](PtrOwn).
156    ///
157    /// # Erasure
158    ///
159    /// This function erases to a cast from raw pointer to shared reference.
160    ///
161    /// ```ignore
162    /// PtrOwn::as_ref(ptr, own)
163    /// // erases to
164    /// & *ptr
165    /// ```
166    #[trusted]
167    #[check(terminates)]
168    #[requires(ptr == own.ptr())]
169    #[ensures(*result == *own.val())]
170    #[allow(unused_variables)]
171    #[intrinsic("ptr_own_as_ref")]
172    pub unsafe fn as_ref(ptr: *const T, own: Ghost<&PtrOwn<T>>) -> &T {
173        unsafe { &*ptr }
174    }
175
176    /// Mutably borrows the underlying `T`.
177    ///
178    /// # Safety
179    ///
180    /// Safety requirements are the same as a direct dereference: `&mut *ptr`.
181    ///
182    /// Creusot will check that all calls to this function are indeed safe: see the
183    /// [type documentation](PtrOwn).
184    ///
185    /// # Erasure
186    ///
187    /// This function erases to a cast from raw pointer to mutable reference.
188    ///
189    /// ```ignore
190    /// PtrOwn::as_mut(ptr, own)
191    /// // erases to
192    /// &mut *ptr
193    /// ```
194    #[trusted]
195    #[check(terminates)]
196    #[allow(unused_variables)]
197    #[requires(ptr as *const T == own.ptr())]
198    #[ensures(*result == *own.val())]
199    #[ensures((^own).ptr() == own.ptr())]
200    #[ensures(*(^own).val() == ^result)]
201    #[intrinsic("ptr_own_as_mut")]
202    pub unsafe fn as_mut(ptr: *mut T, own: Ghost<&mut PtrOwn<T>>) -> &mut T {
203        unsafe { &mut *ptr }
204    }
205
206    /// Transfers ownership of `own` back into a [`Box`].
207    ///
208    /// # Safety
209    ///
210    /// Safety requirements are the same as [`Box::from_raw`].
211    ///
212    /// Creusot will check that all calls to this function are indeed safe: see the
213    /// [type documentation](PtrOwn).
214    #[trusted]
215    #[check(terminates)]
216    #[requires(ptr as *const T == own.ptr())]
217    #[ensures(*result == *own.val())]
218    #[allow(unused_variables)]
219    #[erasure(Box::from_raw)]
220    pub unsafe fn to_box(ptr: *mut T, own: Ghost<PtrOwn<T>>) -> Box<T> {
221        unsafe { Box::from_raw(ptr) }
222    }
223
224    /// Deallocates the memory pointed by `ptr`.
225    ///
226    /// # Safety
227    ///
228    /// Safety requirements are the same as [`Box::from_raw`].
229    ///
230    /// Creusot will check that all calls to this function are indeed safe: see the
231    /// [type documentation](PtrOwn).
232    #[check(terminates)]
233    #[requires(ptr as *const T == own.ptr())]
234    pub unsafe fn drop(ptr: *mut T, own: Ghost<PtrOwn<T>>) {
235        let _ = unsafe { Self::to_box(ptr, own) };
236    }
237
238    /// The pointer of a `PtrOwn` is always aligned.
239    #[check(ghost)]
240    #[ensures(self.ptr().is_aligned_logic())]
241    pub fn ptr_is_aligned_lemma(&self) {}
242
243    /// Opaque wrapper around [`std::ptr::is_aligned_logic`].
244    /// We use this to hide alignment logic by default in `invariant` because it confuses SMT solvers sometimes.
245    /// The underlying property is exposed by [`PtrOwn::ptr_is_aligned_lemma`].
246    #[logic(open(self))]
247    pub fn ptr_is_aligned_opaque(self) -> bool {
248        self.ptr().is_aligned_logic()
249    }
250}