vstd/raw_ptr.rs
1//! Tools and reasoning principles for [raw pointers](https://doc.rust-lang.org/std/primitive.pointer.html).
2//! The tools here are meant to address "real Rust pointers, including all their subtleties on the Rust Abstract Machine,
3//! to the largest extent that is reasonable."
4//!
5//! For a gentler introduction to some of the concepts here, see [`PPtr`](crate::simple_pptr), which uses a much-simplified pointer model.
6//!
7//! ### Pointer model
8//!
9//! A pointer consists of an address (`ptr.addr()` or `ptr as usize`), a provenance `ptr@.provenance`,
10//! and metadata `ptr@.metadata` (which is trivial except for pointers to non-sized types).
11//! Note that in spec code, pointer equality requires *all 3* to be equal, whereas runtime equality (eq)
12//! only compares addresses and metadata.
13//!
14//! `*mut T` vs. `*const T` do not have any semantic difference and Verus treats them as the same;
15//! they can be seamlessly cast to and fro.
16#![allow(unused_imports)]
17use super::layout::*;
18use super::prelude::*;
19
20verus! {
21
22//////////////////////////////////////
23// Define a model of Ptrs and PointsTo
24// Notes on mutability:
25//
26// - Unique vs shared ownership in Verus is always determined
27// via the PointsTo ghost tracked object.
28//
29// - Thus, there is effectively no difference between *mut T and *const T,
30// so we encode both of these in the same way.
31// (In VIR, we distinguish these via a decoration.)
32// Thus we can cast freely between them both in spec and exec code.
33//
34// - This is consistent with Rust's operational semantics;
35// casting between *mut T and *const T has no operational meaning.
36//
37// - When creating a pointer from a reference, the mutability
38// of the pointer *does* have an effect because it determines
39// what kind of "tag" the pointer gets, i.e., whether that
40// tag is readonly or not. In our model here, this tag is folded
41// into the provenance.
42//
43/// Provenance
44///
45/// A full model of provenance is given by formalisms such as "Stacked Borrows"
46/// or "Tree Borrows."
47///
48/// None of these models are finalized, nor has Rust committed to them.
49/// Rust's recent [RFC on provenance](https://rust-lang.github.io/rfcs/3559-rust-has-provenance.html)
50/// simply details that there *is* some concept of provenance.
51///
52/// Our model here, likewise, simply declares `Provenance` as an
53/// abstract type.
54///
55/// MiniRust currently declares a pointer has an `Option<Provenance>`;
56/// the model here gives provenance a special "null" value instead
57/// of using an option.
58///
59/// More reading for reference:
60/// * [https://doc.rust-lang.org/std/ptr/](https://doc.rust-lang.org/std/ptr/)
61/// * [https://github.com/minirust/minirust/tree/master](https://github.com/minirust/minirust/tree/master)
62#[verifier::external_body]
63pub ghost struct Provenance {}
64
65impl Provenance {
66 /// The provenance of the null ptr (or really, "no provenance")
67 pub uninterp spec fn null() -> Self;
68}
69
70/// Metadata
71///
72/// For thin pointers (i.e., when T: Sized), the metadata is `()`.
73/// For slices (`[T]`) and `str`, the metadata is `usize`.
74/// For `dyn` types (not supported by Verus at the time of writing), this type is also nontrivial.
75///
76/// See: <https://doc.rust-lang.org/std/ptr/trait.Pointee.html>
77#[cfg(verus_keep_ghost)]
78pub type Metadata<T> = <T as core::ptr::Pointee>::Metadata;
79
80#[cfg(not(verus_keep_ghost))]
81pub struct FakeMetadata<T: ?Sized> {
82 t: *mut T,
83}
84
85#[cfg(not(verus_keep_ghost))]
86pub type Metadata<T> = FakeMetadata<T>;
87
88/// Model of a pointer `*mut T` or `*const T` in Rust's abstract machine.
89/// In addition to the address, each pointer has its corresponding provenance and metadata.
90#[cfg(verus_keep_ghost)]
91pub ghost struct PtrData<T: core::marker::PointeeSized> {
92 pub addr: usize,
93 pub provenance: Provenance,
94 pub metadata: Metadata<T>,
95}
96
97/**
98Permission to access possibly-initialized, _typed_ memory.
99
100The associated pointer ([`points_to.ptr()`](PointsTo::ptr)) is always a valid pointer for constructing
101a reference to the underlying data. That means it's always aligned to its type
102([`is_aligned`](PointsTo::is_nonnull)) and is non-null ([`is_nonnull`](PointsTo::is_nonnull)).
103
104### Notes
105
106The invariants on a `PointsTo` are a little more restrictive than is necessary for all
107Rust operations you might want to do. For example:
108
1091. With a null pointer to a ZST, Rust lets you read and write (though not take a reference).
110
111```
112#[derive(Copy, Clone)]
113#[repr(align(64))]
114struct X { }
115
116fn zst_test() {
117 let x_ptr: *mut X = std::ptr::null_mut();
118
119 let x = unsafe { *x_ptr }; // allowed
120
121 let x = X { };
122 unsafe { *x_ptr = x; } // allowed
123
124 let j = unsafe { &*x_ptr }; // not allowed because ptr is null
125}
126```
127
1282. The [`std::ptr::read_unaligned`] and [`std::ptr::write_unaligned`] don't require the pointer
129 to be aligned.
130
131Currently, these use-cases aren't supported because `PointsTo` enforces both non-nullness
132and alignment.
133*/
134
135// ptr |--> Init(v) means:
136// bytes in this memory are consistent with value v
137// and we have all the ghost state associated with type V
138//
139// ptr |--> Uninit means:
140// no knowledge about what's in memory
141// (to be pedantic, the bytes might be initialized in rust's abstract machine,
142// but we don't know so we have to pretend they're uninitialized)
143#[verifier::external_body]
144#[verifier::accept_recursive_types(T)]
145pub tracked struct PointsTo<T> {
146 phantom: core::marker::PhantomData<T>,
147 no_copy: NoCopy,
148}
149
150//#[verifier::external_body]
151//#[verifier::accept_recursive_types(T)]
152//pub tracked struct PointsToBytes<T> {
153// phantom: core::marker::PhantomData<T>,
154// no_copy: NoCopy,
155//}
156/// Represents (typed) contents of memory.
157// Don't use std Option here in order to avoid circular dependency issues
158// with verifying the standard library.
159// (Also, using our own enum here lets us have more meaningful
160// variant names like Uninit/Init.)
161#[verifier::accept_recursive_types(T)]
162pub ghost enum MemContents<T> {
163 /// Represents uninitialized memory.
164 Uninit,
165 /// Represents initialized memory with the given value of type `T`.
166 Init(T),
167}
168
169/// Data associated with a `PointsTo` permission.
170/// We keep track of both the pointer and the (potentially uninitialized) value
171/// it points to.
172///
173/// If `opt_value` is `Init(T)`, this signifies that `ptr` points to initialized memory,
174/// and the value of `opt_value` is consistent with the bytes `ptr` points to,
175/// We also have all the ghost state associated with type `T`.
176///
177/// If `opt_value` is `Uninit`, then we have no knowledge about what's in memory,
178/// and we assume `ptr` points to uninitialized memory.
179/// (To be pedantic, the bytes might be initialized in Rust's abstract machine,
180/// but we don't know, so we have to pretend they're uninitialized.)
181pub ghost struct PointsToData<T> {
182 pub ptr: *mut T,
183 pub opt_value: MemContents<T>,
184}
185
186#[cfg(verus_keep_ghost)]
187impl<T: core::marker::PointeeSized> View for *mut T {
188 type V = PtrData<T>;
189
190 uninterp spec fn view(&self) -> Self::V;
191}
192
193/// Compares the address and metadata of two pointers.
194///
195/// Note that this does NOT compare provenance, which does not exist in the runtime
196/// pointer representation (i.e., it only exists in the Rust abstract machine).
197#[cfg(verus_keep_ghost)]
198pub assume_specification<T: core::marker::PointeeSized>[ <*mut T as PartialEq<*mut T>>::eq ](
199 x: &*mut T,
200 y: &*mut T,
201) -> (res: bool)
202 ensures
203 res <==> (x@.addr == y@.addr) && (x@.metadata == y@.metadata),
204;
205
206#[cfg(verus_keep_ghost)]
207impl<T: core::marker::PointeeSized> View for *const T {
208 type V = PtrData<T>;
209
210 #[verifier::inline]
211 open spec fn view(&self) -> Self::V {
212 (*self as *mut T).view()
213 }
214}
215
216/// Compares the address and metadata of two pointers.
217///
218/// Note that this does NOT compare provenance, which does not exist in the runtime
219/// pointer representation (i.e., it only exists in the Rust abstract machine).
220#[cfg(verus_keep_ghost)]
221pub assume_specification<T: core::marker::PointeeSized>[ <*const T as PartialEq<*const T>>::eq ](
222 x: &*const T,
223 y: &*const T,
224) -> (res: bool)
225 ensures
226 res <==> (x@.addr == y@.addr) && (x@.metadata == y@.metadata),
227;
228
229impl<T> View for PointsTo<T> {
230 type V = PointsToData<T>;
231
232 uninterp spec fn view(&self) -> Self::V;
233}
234
235impl<T> PointsTo<T> {
236 /// The pointer that this permission is associated with.
237 #[verifier::inline]
238 pub open spec fn ptr(&self) -> *mut T {
239 self.view().ptr
240 }
241
242 /// The (possibly uninitialized) memory that this permission gives access to.
243 #[verifier::inline]
244 pub open spec fn opt_value(&self) -> MemContents<T> {
245 self.view().opt_value
246 }
247
248 /// Returns `true` if the permission's associated memory is initialized.
249 #[verifier::inline]
250 pub open spec fn is_init(&self) -> bool {
251 self.opt_value().is_init()
252 }
253
254 /// Returns `true` if the permission's associated memory is uninitialized.
255 #[verifier::inline]
256 pub open spec fn is_uninit(&self) -> bool {
257 self.opt_value().is_uninit()
258 }
259
260 /// If the permission's associated memory is initialized,
261 /// returns the value that the pointer points to.
262 /// Otherwise, the result is meaningless.
263 #[verifier::inline]
264 pub open spec fn value(&self) -> T
265 recommends
266 self.is_init(),
267 {
268 self.opt_value().value()
269 }
270
271 /// Guarantee that the `PointsTo` points to a non-null address.
272 pub axiom fn is_nonnull(tracked &self)
273 ensures
274 self@.ptr@.addr != 0,
275 ;
276
277 /// Guarantee that the `PointsTo` points to an aligned address.
278 pub axiom fn is_aligned(tracked &self)
279 ensures
280 self@.ptr@.addr as nat % align_of::<T>() == 0,
281 ;
282
283 /// "Forgets" about the value stored behind the pointer.
284 /// Updates the `PointsTo` value to [`MemContents::Uninit`](MemContents::Uninit).
285 /// Note that this is a `proof` function, i.e.,
286 /// it is operationally a no-op in executable code, even on the Rust Abstract Machine.
287 /// Only the proof-code representation changes.
288 pub axiom fn leak_contents(tracked &mut self)
289 ensures
290 self.ptr() == old(self).ptr(),
291 self.is_uninit(),
292 ;
293
294 /// Guarantees that the memory ranges associated with two permissions will not overlap,
295 /// since you cannot have two permissions to the same memory.
296 ///
297 /// Note: If both S and T are non-zero-sized, then this implies the pointers
298 /// have distinct addresses.
299 pub axiom fn is_disjoint<S>(tracked &mut self, tracked other: &PointsTo<S>)
300 ensures
301 *old(self) == *self,
302 self.ptr() as int + size_of::<T>() <= other.ptr() as int || other.ptr() as int
303 + size_of::<S>() <= self.ptr() as int,
304 ;
305}
306
307impl<T> MemContents<T> {
308 /// Returns `true` if it is a [`MemContents::Init`] value.
309 #[verifier::inline]
310 pub open spec fn is_init(&self) -> bool {
311 self is Init
312 }
313
314 /// Returns `true` if it is a [`MemContents::Uninit`] value.
315 #[verifier::inline]
316 pub open spec fn is_uninit(&self) -> bool {
317 self is Uninit
318 }
319
320 /// If it is a [`MemContents::Init`] value, returns the value.
321 /// Otherwise, the return value is meaningless.
322 #[verifier::inline]
323 pub open spec fn value(&self) -> T
324 recommends
325 self is Init,
326 {
327 self->0
328 }
329}
330
331//////////////////////////////////////
332// Inverse functions:
333// Pointers are equivalent to their model
334/// Constructs a pointer from its underlying model.
335pub uninterp spec fn ptr_mut_from_data<T: core::marker::PointeeSized>(data: PtrData<T>) -> *mut T;
336
337/// Constructs a pointer from its underlying model.
338/// Since `*mut T` and `*const T` are [semantically the same](https://verus-lang.github.io/verus/verusdoc/vstd/raw_ptr/index.html#pointer-model),
339/// we can define this operation in terms of the operation on `*mut T`.
340#[verifier::inline]
341pub open spec fn ptr_from_data<T: core::marker::PointeeSized>(data: PtrData<T>) -> *const T {
342 ptr_mut_from_data(data) as *const T
343}
344
345/// The view of a pointer constructed from `data: PtrData` should be exactly that data.
346pub broadcast axiom fn axiom_ptr_mut_from_data<T: ?Sized>(data: PtrData<T>)
347 ensures
348 (#[trigger] ptr_mut_from_data::<T>(data))@ == data,
349;
350
351// Equiv to ptr_mut_from_data, but named differently to avoid trigger issues
352// Only use for ptrs_mut_eq
353#[doc(hidden)]
354pub uninterp spec fn view_reverse_for_eq<T: ?Sized>(data: PtrData<T>) -> *mut T;
355
356/// Implies that `a@ == b@ ==> a == b`.
357pub broadcast axiom fn ptrs_mut_eq<T: ?Sized>(a: *mut T)
358 ensures
359 view_reverse_for_eq::<T>(#[trigger] a@) == a,
360;
361
362// We do the same trick again, but specialized for Sized types. This improves automation.
363// Specifically, this makes it easier to prove `a == b` without having to explicitly write
364// `a@.metadata == b@.metadata`, since this condition is trivial; both values are always unit.
365// (See the test_extensionality_sized test case.)
366#[doc(hidden)]
367pub closed spec fn view_reverse_for_eq_sized<T>(addr: usize, provenance: Provenance) -> *mut T {
368 view_reverse_for_eq(PtrData { addr: addr, provenance: provenance, metadata: () })
369}
370
371pub broadcast proof fn ptrs_mut_eq_sized<T>(a: *mut T)
372 ensures
373 view_reverse_for_eq_sized::<T>((#[trigger] a@).addr, a@.provenance) == a,
374{
375 assert(a@.metadata == ());
376 ptrs_mut_eq(a);
377}
378
379//////////////////////////////////////
380/// Constructs a null pointer.
381/// NOTE: Trait aliases are not yet supported,
382/// so we use `Pointee<Metadata = ()>` instead of `core::ptr::Thin` here
383#[verifier::inline]
384pub open spec fn ptr_null<
385 T: ::core::marker::PointeeSized + core::ptr::Pointee<Metadata = ()>,
386>() -> *const T {
387 ptr_from_data(PtrData::<T> { addr: 0, provenance: Provenance::null(), metadata: () })
388}
389
390#[cfg(verus_keep_ghost)]
391#[verifier::when_used_as_spec(ptr_null)]
392pub assume_specification<
393 T: core::marker::PointeeSized + core::ptr::Pointee<Metadata = ()>,
394>[ core::ptr::null ]() -> (res: *const T)
395 ensures
396 res == ptr_null::<T>(),
397 opens_invariants none
398 no_unwind
399;
400
401/// Constructs a mutable null pointer.
402/// NOTE: Trait aliases are not yet supported,
403/// so we use `Pointee<Metadata = ()>` instead of `core::ptr::Thin` here
404#[verifier::inline]
405pub open spec fn ptr_null_mut<
406 T: core::marker::PointeeSized + core::ptr::Pointee<Metadata = ()>,
407>() -> *mut T {
408 ptr_mut_from_data(PtrData::<T> { addr: 0, provenance: Provenance::null(), metadata: () })
409}
410
411#[cfg(verus_keep_ghost)]
412#[verifier::when_used_as_spec(ptr_null_mut)]
413pub assume_specification<
414 T: core::marker::PointeeSized + core::ptr::Pointee<Metadata = ()>,
415>[ core::ptr::null_mut ]() -> (res: *mut T)
416 ensures
417 res == ptr_null_mut::<T>(),
418 opens_invariants none
419 no_unwind
420;
421
422//////////////////////////////////////
423// Casting
424// as-casts and implicit casts are translated internally to these functions
425// (including casts that involve *const ptrs)
426/// Cast a pointer to a thin pointer. Address and provenance are preserved; metadata is now thin.
427pub open spec fn spec_cast_ptr_to_thin_ptr<T: ?Sized, U: Sized>(ptr: *mut T) -> *mut U {
428 ptr_mut_from_data(PtrData::<U> { addr: ptr@.addr, provenance: ptr@.provenance, metadata: () })
429}
430
431/// Cast a pointer to a thin pointer. Address and provenance are preserved; metadata is now thin.
432///
433/// Don't call this directly; use an `as`-cast instead.
434#[verifier::external_body]
435#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::raw_ptr::cast_ptr_to_thin_ptr")]
436#[verifier::when_used_as_spec(spec_cast_ptr_to_thin_ptr)]
437pub fn cast_ptr_to_thin_ptr<T: ?Sized, U: Sized>(ptr: *mut T) -> (result: *mut U)
438 ensures
439 result == spec_cast_ptr_to_thin_ptr::<T, U>(ptr),
440 opens_invariants none
441 no_unwind
442{
443 ptr as *mut U
444}
445
446/// Cast a pointer to an array of length `N` to a slice pointer.
447/// Address and provenance are preserved; metadata has length `N`.
448pub open spec fn spec_cast_array_ptr_to_slice_ptr<T, const N: usize>(ptr: *mut [T; N]) -> *mut [T] {
449 ptr_mut_from_data(PtrData::<[T]> { addr: ptr@.addr, provenance: ptr@.provenance, metadata: N })
450}
451
452/// Cast a pointer to an array of length `N` to a slice pointer.
453/// Address and provenance are preserved; metadata has length `N`.
454///
455/// Don't call this directly; use an `as`-cast instead.
456#[verifier::external_body]
457#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::raw_ptr::cast_array_ptr_to_slice_ptr")]
458#[verifier::when_used_as_spec(spec_cast_array_ptr_to_slice_ptr)]
459pub fn cast_array_ptr_to_slice_ptr<T, const N: usize>(ptr: *mut [T; N]) -> (result: *mut [T])
460 ensures
461 result == spec_cast_array_ptr_to_slice_ptr(ptr),
462 opens_invariants none
463 no_unwind
464{
465 ptr as *mut [T]
466}
467
468/// Cast a slice pointer to another slice pointer.
469/// Length is preserved even if the size of the elements changes.
470pub open spec fn spec_cast_slice_ptr_to_slice_ptr<T, U>(ptr: *mut [T]) -> *mut [U] {
471 ptr_mut_from_data(
472 PtrData::<[U]> { addr: ptr@.addr, provenance: ptr@.provenance, metadata: ptr@.metadata },
473 )
474}
475
476/// Cast a slice pointer to another slice pointer.
477/// Length is preserved even if the size of the elements changes.
478///
479/// Don't call this directly; use an `as`-cast instead.
480#[verifier::external_body]
481#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::raw_ptr::cast_slice_ptr_to_slice_ptr")]
482#[verifier::when_used_as_spec(spec_cast_slice_ptr_to_slice_ptr)]
483pub fn cast_slice_ptr_to_slice_ptr<T, U>(ptr: *mut [T]) -> (result: *mut [U])
484 ensures
485 result == spec_cast_slice_ptr_to_slice_ptr::<T, U>(ptr),
486 opens_invariants none
487 no_unwind
488{
489 ptr as *mut [U]
490}
491
492/// Cast a slice pointer to a `str` pointer.
493/// Length is preserved even if the size of the elements changes.
494pub open spec fn spec_cast_slice_ptr_to_str_ptr<T>(ptr: *mut [T]) -> *mut str {
495 ptr_mut_from_data(
496 PtrData::<str> { addr: ptr@.addr, provenance: ptr@.provenance, metadata: ptr@.metadata },
497 )
498}
499
500/// Cast a slice pointer to a `str` pointer.
501/// Length is preserved even if the size of the elements changes.
502///
503/// Don't call this directly; use an `as`-cast instead.
504#[verifier::external_body]
505#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::raw_ptr::cast_slice_ptr_to_str_ptr")]
506#[verifier::when_used_as_spec(spec_cast_slice_ptr_to_str_ptr)]
507pub fn cast_slice_ptr_to_str_ptr<T>(ptr: *mut [T]) -> (result: *mut str)
508 ensures
509 result == spec_cast_slice_ptr_to_str_ptr::<T>(ptr),
510 opens_invariants none
511 no_unwind
512{
513 ptr as *mut str
514}
515
516/// Cast a `str` pointer to a slice pointer.
517/// Length is preserved even if the size of the elements changes.
518pub open spec fn spec_cast_str_ptr_to_slice_ptr<T>(ptr: *mut str) -> *mut [T] {
519 ptr_mut_from_data(
520 PtrData::<[T]> { addr: ptr@.addr, provenance: ptr@.provenance, metadata: ptr@.metadata },
521 )
522}
523
524/// Cast a `str` pointer to a slice pointer.
525/// Length is preserved even if the size of the elements changes.
526///
527/// Don't call this directly; use an `as`-cast instead.
528#[verifier::external_body]
529#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::raw_ptr::cast_str_ptr_to_slice_ptr")]
530#[verifier::when_used_as_spec(spec_cast_str_ptr_to_slice_ptr)]
531pub fn cast_str_ptr_to_slice_ptr<T>(ptr: *mut str) -> (result: *mut [T])
532 ensures
533 result == spec_cast_str_ptr_to_slice_ptr::<T>(ptr),
534 opens_invariants none
535 no_unwind
536{
537 ptr as *mut [T]
538}
539
540/// Cast a pointer to a `usize` by extracting just the address.
541pub open spec fn spec_cast_ptr_to_usize<T: Sized>(ptr: *mut T) -> usize {
542 ptr@.addr
543}
544
545/// Cast the address of a pointer to a `usize`.
546///
547/// Don't call this directly; use an `as`-cast instead.
548#[verifier::external_body]
549#[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::raw_ptr::cast_ptr_to_usize")]
550#[verifier::when_used_as_spec(spec_cast_ptr_to_usize)]
551pub fn cast_ptr_to_usize<T: Sized>(ptr: *mut T) -> (result: usize)
552 ensures
553 result == spec_cast_ptr_to_usize(ptr),
554 opens_invariants none
555 no_unwind
556{
557 ptr as usize
558}
559
560//////////////////////////////////////
561// Reading and writing
562/// Calls [`core::ptr::write`] to write the value `v` to the memory location pointed to by `ptr`,
563/// using the permission `perm`.
564///
565/// This does _not_ drop the contents. If the memory is already initialized,
566/// this could leak allocations or resources, so care should be taken to not overwrite an object that should be dropped.
567/// This is appropriate for initializing uninitialized memory, or overwriting memory that has previously been [read](ptr_mut_read) from.
568#[inline(always)]
569#[verifier::external_body]
570pub fn ptr_mut_write<T>(ptr: *mut T, Tracked(perm): Tracked<&mut PointsTo<T>>, v: T)
571 requires
572 old(perm).ptr() == ptr,
573 ensures
574 perm.ptr() == ptr,
575 perm.opt_value() == MemContents::Init(v),
576 opens_invariants none
577 no_unwind
578{
579 unsafe {
580 core::ptr::write(ptr, v);
581 }
582}
583
584/// Calls [`core::ptr::read`] to read from the memory pointed to by `ptr`,
585/// using the permission `perm`.
586///
587/// This leaves the data as "unitialized", i.e., performs a move.
588///
589/// TODO: This needs to be made more general (i.e., should be able to read a Copy type
590/// without destroying it; should be able to leave the bytes intact without uninitializing them).
591#[inline(always)]
592#[verifier::external_body]
593pub fn ptr_mut_read<T>(ptr: *const T, Tracked(perm): Tracked<&mut PointsTo<T>>) -> (v: T)
594 requires
595 old(perm).ptr() == ptr,
596 old(perm).is_init(),
597 ensures
598 perm.ptr() == ptr,
599 perm.is_uninit(),
600 v == old(perm).value(),
601 opens_invariants none
602 no_unwind
603{
604 unsafe { core::ptr::read(ptr) }
605}
606
607/// Equivalent to `&*ptr`, passing in a permission `perm` to ensure safety.
608/// The memory pointed to by `ptr` must be initialized.
609#[inline(always)]
610#[verifier::external_body]
611pub fn ptr_ref<T>(ptr: *const T, Tracked(perm): Tracked<&PointsTo<T>>) -> (v: &T)
612 requires
613 perm.ptr() == ptr,
614 perm.is_init(),
615 ensures
616 v == perm.value(),
617 opens_invariants none
618 no_unwind
619{
620 unsafe { &*ptr }
621}
622
623/* coming soon
624/// Equivalent to &mut *X, passing in a permission `perm` to ensure safety.
625/// The memory pointed to by `ptr` must be initialized.
626#[inline(always)]
627#[verifier::external_body]
628pub fn ptr_mut_ref<T>(ptr: *mut T, Tracked(perm): Tracked<&mut PointsTo<T>>) -> (v: &mut T)
629 requires
630 old(perm).ptr() == ptr,
631 old(perm).is_init()
632 ensures
633 perm.ptr() == ptr,
634 perm.is_init(),
635
636 old(perm).value() == *old(v),
637 new(perm).value() == *new(v),
638 unsafe { &*ptr }
639}
640*/
641
642macro_rules! pointer_specs {
643 ($mod_ident:ident, $ptr_from_data:ident, $mu:tt) => {
644 #[cfg(verus_keep_ghost)]
645 mod $mod_ident {
646 use super::*;
647
648 verus!{
649
650 #[verifier::inline]
651 pub open spec fn spec_addr<T: ::core::marker::PointeeSized>(p: *$mu T) -> usize { p@.addr }
652
653 #[verifier::when_used_as_spec(spec_addr)]
654 #[cfg(verus_keep_ghost)]
655 pub assume_specification<T: ::core::marker::PointeeSized>[<*$mu T>::addr](p: *$mu T) -> (addr: usize)
656 ensures addr == spec_addr(p)
657 opens_invariants none
658 no_unwind;
659
660 pub open spec fn spec_with_addr<T: ::core::marker::PointeeSized>(p: *$mu T, addr: usize) -> *$mu T {
661 $ptr_from_data(PtrData::<T> { addr: addr, .. p@ })
662 }
663
664 #[verifier::when_used_as_spec(spec_with_addr)]
665 #[cfg(verus_keep_ghost)]
666 pub assume_specification<T: ::core::marker::PointeeSized>[<*$mu T>::with_addr](p: *$mu T, addr: usize) -> (q: *$mu T)
667 ensures q == spec_with_addr(p, addr)
668 opens_invariants none
669 no_unwind;
670
671 }
672 }
673 };
674}
675
676pointer_specs!(ptr_mut_specs, ptr_mut_from_data, mut);
677
678pointer_specs!(ptr_const_specs, ptr_from_data, const);
679
680pub broadcast group group_raw_ptr_axioms {
681 axiom_ptr_mut_from_data,
682 ptrs_mut_eq,
683 ptrs_mut_eq_sized,
684}
685
686/// Tracked object that indicates a given provenance has been exposed.
687#[verifier::external_body]
688pub tracked struct IsExposed {}
689
690impl Clone for IsExposed {
691 #[verifier::external_body]
692 fn clone(&self) -> (s: Self)
693 ensures
694 s == self,
695 {
696 IsExposed { }
697 }
698}
699
700impl Copy for IsExposed {
701
702}
703
704impl IsExposed {
705 /// The view of `IsExposed` is simply its provenance.
706 pub open spec fn view(self) -> Provenance {
707 self.provenance()
708 }
709
710 /// Provenance we are exposing.
711 pub uninterp spec fn provenance(self) -> Provenance;
712
713 /// It is always possible to expose/construct the null provenance.
714 pub axiom fn null() -> (tracked exp: IsExposed)
715 ensures
716 exp.provenance() == Provenance::null(),
717 ;
718}
719
720/// Perform a provenance expose operation.
721#[verifier::external_body]
722pub fn expose_provenance<T: Sized>(m: *mut T) -> (provenance: Tracked<IsExposed>)
723 ensures
724 provenance@@ == m@.provenance,
725 opens_invariants none
726 no_unwind
727{
728 let _ = m as usize;
729 Tracked::assume_new()
730}
731
732/// Construct a pointer with the given provenance from a _usize_ address.
733/// The provenance must have previously been exposed.
734#[verifier::external_body]
735pub fn with_exposed_provenance<T: Sized>(
736 addr: usize,
737 Tracked(provenance): Tracked<IsExposed>,
738) -> (p: *mut T)
739 ensures
740 p == ptr_mut_from_data::<T>(
741 PtrData::<T> { addr: addr, provenance: provenance@, metadata: () },
742 ),
743 opens_invariants none
744 no_unwind
745{
746 addr as *mut T
747}
748
749/// Variable-sized uninitialized memory.
750///
751/// Permission is for an arbitrary set of addresses, not necessarily contiguous,
752/// and with a given provenance.
753// Note reading from uninitialized memory is UB, so we shouldn't give any
754// reading capabilities to PointsToRaw. Turning a PointsToRaw into a PointsTo
755// should always leave it as 'uninitialized'.
756#[verifier::external_body]
757pub tracked struct PointsToRaw {
758 // TODO implement this as Map<usize, PointsTo<u8>> or something
759 no_copy: NoCopy,
760}
761
762impl PointsToRaw {
763 /// Provenance of the `PointsToRaw` permission;
764 /// this corresponds to the original allocation and does not change.
765 pub uninterp spec fn provenance(self) -> Provenance;
766
767 /// Memory addresses (domain) that the `PointsToRaw` permission gives access to.
768 /// This set may be split apart and/or recombined, in order to create permissions to smaller pieces of the allocation.
769 pub uninterp spec fn dom(self) -> Set<int>;
770
771 /// Returns `true` if the domain of this permission is exactly the range `[start, start + len)`.
772 pub open spec fn is_range(self, start: int, len: int) -> bool {
773 super::set_lib::set_int_range(start, start + len) =~= self.dom()
774 }
775
776 /// Returns `true` if the domain of this permission contains the range `[start, start + len)`.
777 pub open spec fn contains_range(self, start: int, len: int) -> bool {
778 super::set_lib::set_int_range(start, start + len) <= self.dom()
779 }
780
781 /// Constructs a `PointsToRaw` permission over an empty domain with the given provenance.
782 pub axiom fn empty(provenance: Provenance) -> (tracked points_to_raw: Self)
783 ensures
784 points_to_raw.dom() == Set::<int>::empty(),
785 points_to_raw.provenance() == provenance,
786 ;
787
788 /// Splits the current `PointsToRaw` permission into a permission with domain `range`
789 /// and a permission containing the rest of the domain,
790 /// provided that `range` is contained in the domain of the current permission.
791 pub axiom fn split(tracked self, range: Set<int>) -> (tracked res: (Self, Self))
792 requires
793 range.subset_of(self.dom()),
794 ensures
795 res.0.provenance() == self.provenance(),
796 res.1.provenance() == self.provenance(),
797 res.0.dom() == range,
798 res.1.dom() == self.dom().difference(range),
799 ;
800
801 /// Joins two `PointsToRaw` permissions into one,
802 /// provided that they have the same provenance.
803 /// The memory addresses of the new permission is the union of the domains of `self` and `other`.
804 pub axiom fn join(tracked self, tracked other: Self) -> (tracked joined: Self)
805 requires
806 self.provenance() == other.provenance(),
807 ensures
808 joined.provenance() == self.provenance(),
809 joined.dom() == self.dom() + other.dom(),
810 ;
811
812 /// Creates a `PointsTo<V>` permission from a `PointsToRaw` permission
813 /// with address `start` and the same provanance as the `PointsToRaw` permission,
814 /// provided that `start` is aligned to `V` and
815 /// that the domain of the `PointsToRaw` permission matches the size of `V`.
816 ///
817 /// In combination with [`PointsToRaw::empty()`],
818 /// this lets us create a PointsTo for a ZST for _any_ non-null aligned pointer.
819 ///
820 /// To call this, it is necessary for the address to be non-null. This can be proved either
821 /// by showing `start != 0` or by showing the size of the type is non-zero (in which case
822 /// the non-null-ness follows from the existence of the `PointsToRaw`).
823 pub axiom fn into_typed<V>(tracked self, start: usize) -> (tracked points_to: PointsTo<V>)
824 requires
825 start != 0 || size_of::<V>() != 0,
826 start as int % align_of::<V>() as int == 0,
827 self.is_range(start as int, size_of::<V>() as int),
828 ensures
829 points_to.ptr() == ptr_mut_from_data::<V>(
830 PtrData { addr: start, provenance: self.provenance(), metadata: () },
831 ),
832 points_to.is_uninit(),
833 ;
834}
835
836impl<V> PointsTo<V> {
837 /// Creates a `PointsToRaw` from a `PointsTo<V>` with the same provenance
838 /// and a range corresponding to the address of the `PointsTo<V>` and size of `V`,
839 /// provided that the memory tracked by the `PointsTo<V>`is uninitialized.
840 pub axiom fn into_raw(tracked self) -> (tracked points_to_raw: PointsToRaw)
841 requires
842 self.is_uninit(),
843 ensures
844 points_to_raw.is_range(self.ptr().addr() as int, size_of::<V>() as int),
845 points_to_raw.provenance() == self.ptr()@.provenance,
846 ;
847}
848
849// Allocation and deallocation via the global allocator
850/// Permission to perform a deallocation with the global allocator.
851#[verifier::external_body]
852pub tracked struct Dealloc {
853 no_copy: NoCopy,
854}
855
856/// Data associated with a `Dealloc` permission.
857pub ghost struct DeallocData {
858 pub addr: usize,
859 pub size: nat,
860 pub align: nat,
861 /// This should probably be some kind of "allocation ID" (with "allocation ID" being
862 /// only one part of a full Provenance definition).
863 pub provenance: Provenance,
864}
865
866impl Dealloc {
867 pub uninterp spec fn view(self) -> DeallocData;
868
869 /// Start address of the allocation you are allowed to deallocate.
870 #[verifier::inline]
871 pub open spec fn addr(self) -> usize {
872 self.view().addr
873 }
874
875 /// Size of the allocation you are allowed to deallocate.
876 #[verifier::inline]
877 pub open spec fn size(self) -> nat {
878 self.view().size
879 }
880
881 /// Alignment of the allocation you are allowed to deallocate.
882 #[verifier::inline]
883 pub open spec fn align(self) -> nat {
884 self.view().align
885 }
886
887 /// Provenance of the allocation you are allowed to deallocate.
888 #[verifier::inline]
889 pub open spec fn provenance(self) -> Provenance {
890 self.view().provenance
891 }
892}
893
894/// Allocate with the global allocator.
895/// The precondition should be consistent with the [documented safety conditions on `alloc`](https://doc.rust-lang.org/alloc/alloc/trait.GlobalAlloc.html#tymethod.alloc).
896/// Returns a pointer with a corresponding [`PointsToRaw`] and [`Dealloc`] permissions.
897#[cfg(feature = "std")]
898#[verifier::external_body]
899pub fn allocate(size: usize, align: usize) -> (pt: (
900 *mut u8,
901 Tracked<PointsToRaw>,
902 Tracked<Dealloc>,
903))
904 requires
905 valid_layout(size, align),
906 size != 0,
907 ensures
908 pt.1@.is_range(pt.0.addr() as int, size as int),
909 pt.0.addr() + size <= usize::MAX + 1,
910 pt.2@@ == (DeallocData {
911 addr: pt.0.addr(),
912 size: size as nat,
913 align: align as nat,
914 provenance: pt.1@.provenance(),
915 }),
916 pt.0.addr() as int % align as int == 0,
917 pt.0@.provenance == pt.1@.provenance(),
918 opens_invariants none
919{
920 // SAFETY: valid_layout is a precondition
921 let layout = unsafe { alloc::alloc::Layout::from_size_align_unchecked(size, align) };
922 // SAFETY: size != 0
923 let p = unsafe { ::alloc::alloc::alloc(layout) };
924 if p == core::ptr::null_mut() {
925 std::process::abort();
926 }
927 (p, Tracked::assume_new(), Tracked::assume_new())
928}
929
930/// Deallocate with the global allocator.
931///
932/// The [`Dealloc`] permission ensures that the
933/// [documented safety conditions on `dealloc`](https://doc.rust-lang.org/1.82.0/core/alloc/trait.GlobalAlloc.html#tymethod.dealloc)
934/// are satisfied; by also giving up permission of the [`PointsToRaw`] permission,
935/// we ensure there can be no use-after-free bug as a result of this deallocation.
936/// In order to do so, the parameters of the [`PointsToRaw`] and [`Dealloc`] permissions must match the parameters of the deallocation.
937#[cfg(feature = "alloc")]
938#[verifier::external_body]
939pub fn deallocate(
940 p: *mut u8,
941 size: usize,
942 align: usize,
943 Tracked(pt): Tracked<PointsToRaw>,
944 Tracked(dealloc): Tracked<Dealloc>,
945)
946 requires
947 dealloc.addr() == p.addr(),
948 dealloc.size() == size,
949 dealloc.align() == align,
950 dealloc.provenance() == pt.provenance(),
951 pt.is_range(dealloc.addr() as int, dealloc.size() as int),
952 p@.provenance == dealloc.provenance(),
953 opens_invariants none
954{
955 // SAFETY: ensured by dealloc token
956 let layout = unsafe { alloc::alloc::Layout::from_size_align_unchecked(size, align) };
957 unsafe {
958 ::alloc::alloc::dealloc(p, layout);
959 }
960}
961
962/// This is meant to be a replacement for `&'a T` that allows Verus to keep track of
963/// not just the `T` value but the pointer as well.
964/// It would be better to get rid of this and use normal reference types `&'a T`,
965/// but there are a lot of unsolved implementation questions.
966/// The existence of `SharedReference<'a, T>` is a stop-gap.
967#[verifier::external_body]
968#[verifier::accept_recursive_types(T)]
969pub struct SharedReference<'a, T>(&'a T);
970
971impl<'a, T> Clone for SharedReference<'a, T> {
972 #[verifier::external_body]
973 fn clone(&self) -> (ret: Self)
974 ensures
975 ret == *self,
976 {
977 SharedReference(self.0)
978 }
979}
980
981impl<'a, T> Copy for SharedReference<'a, T> {
982
983}
984
985impl<'a, T> SharedReference<'a, T> {
986 pub uninterp spec fn value(self) -> T;
987
988 pub uninterp spec fn ptr(self) -> *const T;
989
990 #[verifier::external_body]
991 fn new(t: &'a T) -> (s: Self)
992 ensures
993 s.value() == t,
994 {
995 SharedReference(t)
996 }
997
998 #[verifier::external_body]
999 fn as_ref(self) -> (t: &'a T)
1000 ensures
1001 t == self.value(),
1002 {
1003 self.0
1004 }
1005
1006 #[verifier::external_body]
1007 fn as_ptr(self) -> (ptr: *const T)
1008 ensures
1009 ptr == self.ptr(),
1010 {
1011 &*self.0
1012 }
1013
1014 pub axiom fn points_to(tracked self) -> (tracked pt: &'a PointsTo<T>)
1015 ensures
1016 pt.ptr() == self.ptr(),
1017 pt.is_init(),
1018 pt.value() == self.value(),
1019 ;
1020}
1021
1022/// Like [`ptr_ref`] but returns a `SharedReference` so it keeps track of the relationship
1023/// between the pointers.
1024/// Note the resulting reference's pointers does NOT have the same provenance.
1025/// This is because in Rust models like Stacked Borrows / Tree Borrows, the pointer
1026/// gets a new tag.
1027#[inline(always)]
1028#[verifier::external_body]
1029pub fn ptr_ref2<'a, T>(ptr: *const T, Tracked(perm): Tracked<&PointsTo<T>>) -> (v: SharedReference<
1030 'a,
1031 T,
1032>)
1033 requires
1034 perm.ptr() == ptr,
1035 perm.is_init(),
1036 ensures
1037 v.value() == perm.value(),
1038 v.ptr().addr() == ptr.addr(),
1039 v.ptr()@.metadata == ptr@.metadata,
1040 opens_invariants none
1041 no_unwind
1042{
1043 SharedReference(unsafe { &*ptr })
1044}
1045
1046} // verus!