Skip to content

Commit 050c2e1

Browse files
authored
Prove NonNullPtr APIs (#258)
1 parent 8a216b6 commit 050c2e1

File tree

3 files changed

+132
-15
lines changed

3 files changed

+132
-15
lines changed

ostd/src/sync/rcu/non_null/mod.rs

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ pub unsafe trait NonNullPtr: Sized +'static {
5757
/// SOUNDNESS: Considering also returning the Dealloc permission to ensure no memory leak.
5858
fn into_raw(self) -> (ret:(NonNull<Self::Target>, Tracked<SmartPtrPointsTo<Self::Target>>))
5959
ensures
60+
ptr_mut_from_nonull(ret.0) == self.ptr_mut_spec(),
6061
ptr_mut_from_nonull(ret.0) == ret.1@.ptr(),
6162
ret.1@.inv(),
6263
Self::match_points_to_type(ret.1@),
@@ -78,11 +79,13 @@ pub unsafe trait NonNullPtr: Sized +'static {
7879
/// so we can do nothing with the raw pointer because of the absence of permission.
7980
/// VERUS LIMITATION: the #[verus_spec] attribute does not support `with` in trait yet.
8081
/// SOUNDNESS: Considering consuming the Dealloc permission to ensure no double free.
81-
unsafe fn from_raw(ptr: NonNull<Self::Target>, perm: Tracked<SmartPtrPointsTo<Self::Target>>) -> Self
82+
unsafe fn from_raw(ptr: NonNull<Self::Target>, perm: Tracked<SmartPtrPointsTo<Self::Target>>) -> (ret:Self)
8283
requires
8384
Self::match_points_to_type(perm@),
8485
ptr_mut_from_nonull(ptr) == perm@.ptr(),
8586
perm@.inv(),
87+
ensures
88+
ptr_mut_from_nonull(ptr) == ret.ptr_mut_spec(),
8689
;
8790

8891
// VERUS LIMITATION: Cannot use associated type with lifetime yet, will implement it as a free function for each type.
@@ -99,6 +102,9 @@ pub unsafe trait NonNullPtr: Sized +'static {
99102
fn ref_as_raw(ptr_ref: Self::Ref<'_>) -> NonNull<Self::Target>;*/
100103

101104
spec fn match_points_to_type(perm: SmartPtrPointsTo<Self::Target>) -> bool;
105+
106+
// A uninterpreted spec function that returns the inner raw pointer.
107+
spec fn ptr_mut_spec(self) -> *mut Self::Target;
102108
}
103109

104110
/// A type that represents `&'a Box<T>`.
@@ -122,6 +128,10 @@ impl<'a, T> BoxRef<'a, T> {
122128
pub closed spec fn ptr(self) -> *mut T {
123129
self.inner
124130
}
131+
132+
pub closed spec fn value(self) -> T {
133+
self.v_perm@.value()
134+
}
125135
}
126136

127137
/*
@@ -138,16 +148,24 @@ impl<T> Deref for BoxRef<'_, T> {
138148
}
139149
}
140150
*/
141-
/*
151+
152+
#[verus_verify]
142153
impl<'a, T> BoxRef<'a, T> {
143154
/// Dereferences `self` to get a reference to `T` with the lifetime `'a`.
155+
#[verus_spec(ret => ensures *ret == self.value())]
144156
pub fn deref_target(&self) -> &'a T {
145-
// SAFETY: The reference is created through `NonNullPtr::raw_as_ref`, hence
157+
// [VERIFIED] SAFETY: The reference is created through `NonNullPtr::raw_as_ref`, hence
146158
// the original owned pointer and target must outlive the lifetime parameter `'a`,
147159
// and during `'a` no mutable references to the pointer will exist.
148-
unsafe { &*(self.inner) }
160+
161+
let Tracked(perm) = self.v_perm;
162+
proof!{
163+
use_type_invariant(self);
164+
}
165+
//unsafe { &*(self.inner) }
166+
vstd::raw_ptr::ptr_ref(self.inner, Tracked(perm.tracked_borrow_points_to())) // The function body of ptr_ref is exactly the same as `unsafe { &*(self.inner) }`
149167
}
150-
}*/
168+
}
151169

152170
#[verus_verify]
153171
unsafe impl<T: 'static> NonNullPtr for Box<T> {
@@ -176,7 +194,7 @@ unsafe impl<T: 'static> NonNullPtr for Box<T> {
176194

177195
unsafe fn from_raw(ptr: NonNull<Self::Target>, Tracked(perm): Tracked<SmartPtrPointsTo<Self::Target>>) -> Self {
178196
proof_decl!{
179-
let tracked perm = perm.get_box_points_to().tracked_get_perm();
197+
let tracked perm = perm.get_box_points_to().tracked_get_points_to_with_dealloc();
180198
}
181199
let ptr = ptr.as_ptr();
182200

@@ -200,6 +218,10 @@ unsafe impl<T: 'static> NonNullPtr for Box<T> {
200218
open spec fn match_points_to_type(perm: SmartPtrPointsTo<Self::Target>) -> bool {
201219
perm is Box
202220
}
221+
222+
open spec fn ptr_mut_spec(self) -> *mut Self::Target {
223+
box_pointer_spec(self)
224+
}
203225
}
204226

205227
pub fn box_ref_as_raw<T: 'static>(ptr_ref: BoxRef<'_ ,T>) -> (ret:(NonNull<T>, Tracked<&BoxPointsTo<T>>))
@@ -233,20 +255,47 @@ pub unsafe fn box_raw_as_ref<'a, T: 'static>(raw: NonNull<T>, perm: Tracked<&'a
233255
/// A type that represents `&'a Arc<T>`.
234256
#[verus_verify]
235257
#[derive(Debug)]
236-
pub struct ArcRef<'a, T> {
258+
pub struct ArcRef<'a, T: 'static> {
237259
inner: ManuallyDrop<Arc<T>>,
238260
_marker: PhantomData<&'a Arc<T>>,
261+
v_perm: Tracked<ArcPointsTo<T>>,
239262
}
240263

241-
/*
264+
impl<'a, T> ArcRef<'a, T> {
265+
#[verifier::type_invariant]
266+
spec fn type_inv(self) -> bool {
267+
&&& self.ptr() == self.v_perm@.ptr()
268+
&&& self.v_perm@.inv()
269+
&&& self.ptr()@.addr != 0
270+
&&& self.ptr()@.addr as int % vstd::layout::align_of::<T>() as int == 0
271+
}
272+
273+
pub open spec fn ptr(self) -> *const T {
274+
arc_pointer_spec(*self.deref_as_arc_spec())
275+
}
276+
277+
pub closed spec fn deref_as_arc_spec(&self) -> &Arc<T> {
278+
manually_drop_deref_spec(&self.inner)
279+
}
280+
281+
/// A workaround that Verus does not support implementing spec for Deref trait yet.
282+
pub broadcast axiom fn arcref_deref_spec_eq(self)
283+
ensures
284+
#[trigger] self.deref_as_arc_spec() == #[trigger] self.deref_spec(),
285+
;
286+
}
287+
288+
#[verus_verify]
242289
impl<T> Deref for ArcRef<'_, T> {
243290
type Target = Arc<T>;
244291

292+
#[verus_verify]
245293
fn deref(&self) -> &Self::Target {
246294
&self.inner
247295
}
248296
}
249297

298+
/*
250299
impl<'a, T> ArcRef<'a, T> {
251300
/// Dereferences `self` to get a reference to `T` with the lifetime `'a`.
252301
pub fn deref_target(&self) -> &'a T {
@@ -313,6 +362,26 @@ unsafe impl<T: 'static> NonNullPtr for Arc<T> {
313362
open spec fn match_points_to_type(perm: SmartPtrPointsTo<Self::Target>) -> bool {
314363
perm is Arc
315364
}
365+
366+
open spec fn ptr_mut_spec(self) -> *mut Self::Target {
367+
arc_pointer_spec(self) as *mut Self::Target
368+
}
369+
}
370+
371+
pub fn arc_ref_as_raw<T: 'static>(ptr_ref: ArcRef<'_ ,T>) -> (ret:(NonNull<T>, Tracked<ArcPointsTo<T>>))
372+
ensures
373+
ret.0 == nonnull_from_ptr_mut(ptr_ref.ptr() as *mut T),
374+
ret.1@.ptr().addr() != 0,
375+
ret.1@.ptr().addr() as int % vstd::layout::align_of::<T>() as int == 0,
376+
ret.1@.ptr() == ptr_mut_from_nonull(ret.0),
377+
ret.1@.inv(),
378+
{
379+
proof!{
380+
use_type_invariant(&ptr_ref);
381+
}
382+
// NonNullPtr::into_raw(ManuallyDrop::into_inner(ptr_ref.inner))
383+
let (ptr, Tracked(perm)) = NonNullPtr::into_raw(ManuallyDrop::into_inner(ptr_ref.inner));
384+
(ptr, Tracked(perm.get_arc_points_to()))
316385
}
317386

318387
/*

vstd_extra/src/external/smart_ptr.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,39 @@ impl<T> BoxPointsTo<T> {
4747
self.perm.is_init()
4848
}
4949

50-
pub proof fn tracked_get_perm(tracked self) -> (tracked ret: PointsTowithDealloc<T>)
50+
pub open spec fn value(self) -> T {
51+
self.perm.value()
52+
}
53+
54+
pub proof fn tracked_get_points_to_with_dealloc(tracked self) -> (tracked ret:
55+
PointsTowithDealloc<T>)
5156
returns
5257
self.perm,
5358
{
5459
self.perm
5560
}
5661

57-
pub proof fn tracked_borrow_perm<'a>(tracked &'a self) -> (tracked ret: &'a PointsTowithDealloc<
58-
T,
59-
>)
60-
ensures
61-
ret == &self.perm,
62+
pub proof fn tracked_borrow_points_to_with_dealloc(tracked &self) -> (tracked ret:
63+
&PointsTowithDealloc<T>)
6264
returns
6365
&self.perm,
6466
{
6567
&self.perm
6668
}
69+
70+
pub proof fn tracked_get_points_to(tracked self) -> (tracked ret: PointsTo<T>)
71+
returns
72+
self.perm.points_to,
73+
{
74+
self.tracked_get_points_to_with_dealloc().tracked_get_points_to()
75+
}
76+
77+
pub proof fn tracked_borrow_points_to(tracked &self) -> (tracked ret: &PointsTo<T>)
78+
returns
79+
&self.perm.points_to,
80+
{
81+
&self.tracked_borrow_points_to_with_dealloc().tracked_borrow_points_to()
82+
}
6783
}
6884

6985
impl<T> ArcPointsTo<T> {
@@ -87,7 +103,11 @@ impl<T> ArcPointsTo<T> {
87103
self.perm.is_init()
88104
}
89105

90-
pub proof fn tracked_get_perm(tracked self) -> (tracked ret: &'static PointsTo<T>)
106+
pub open spec fn value(self) -> T {
107+
self.perm.value()
108+
}
109+
110+
pub proof fn tracked_borrow_points_to(tracked &self) -> (tracked ret: &'static PointsTo<T>)
91111
returns
92112
self.perm,
93113
{
@@ -218,6 +238,8 @@ impl<T> SmartPtrPointsTo<T> {
218238
}
219239
}
220240

241+
pub uninterp spec fn box_pointer_spec<T>(b: Box<T>) -> *mut T;
242+
221243
// VERUS LIMITATION: can not add ghost parameter in external specification yet, sp we wrap it in an exterbnal_body function
222244
// The memory layout ensures that Box<T> has the following properties:
223245
// 1. The pointer is aligned.
@@ -228,6 +250,7 @@ impl<T> SmartPtrPointsTo<T> {
228250
#[verifier::external_body]
229251
pub fn box_into_raw<T>(b: Box<T>) -> (ret: (*mut T, Tracked<PointsTo<T>>, Tracked<Option<Dealloc>>))
230252
ensures
253+
ret.0 == box_pointer_spec(b),
231254
ret.0 == ret.1@.ptr(),
232255
ret.1@.ptr().addr() != 0,
233256
ret.1@.is_init(),
@@ -269,15 +292,20 @@ pub unsafe fn box_from_raw<T>(
269292
},
270293
None => { &&& vstd::layout::size_of::<T>() == 0 },
271294
},
295+
ensures
296+
box_pointer_spec(ret) == ptr,
272297
{
273298
unsafe { Box::from_raw(ptr) }
274299
}
275300

301+
pub uninterp spec fn arc_pointer_spec<T>(a: Arc<T>) -> *const T;
302+
276303
// VERUS LIMITATION: can not add ghost parameter in external specification yet, sp we wrap it in an exterbnal_body function
277304
// `Arc::into_raw` will not decrease the reference count, so the memory will keep valid until we convert back to Arc<T> and drop it.
278305
#[verifier::external_body]
279306
pub fn arc_into_raw<T>(p: Arc<T>) -> (ret: (*const T, Tracked<ArcPointsTo<T>>))
280307
ensures
308+
ret.0 == arc_pointer_spec(p),
281309
ret.0 == ret.1@.ptr(),
282310
ret.1@.ptr().addr() != 0,
283311
ret.1@.is_init(),
@@ -296,6 +324,8 @@ pub unsafe fn arc_from_raw<T>(ptr: *const T, tracked points_to: Tracked<ArcPoint
296324
points_to@.ptr() == ptr,
297325
points_to@.is_init(),
298326
points_to@.ptr().addr() as int % vstd::layout::align_of::<T>() as int == 0,
327+
ensures
328+
arc_pointer_spec(ret) == ptr,
299329
{
300330
unsafe { Arc::from_raw(ptr) }
301331
}

vstd_extra/src/raw_ptr_extra.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,31 @@ impl<T> PointsTowithDealloc<T> {
7575
self.points_to.is_uninit()
7676
}
7777

78+
pub open spec fn value(self) -> T {
79+
self.points_to.value()
80+
}
81+
7882
pub open spec fn dealloc_aligned(self) -> bool {
7983
match self.dealloc {
8084
Some(dealloc) => { dealloc.align() == vstd::layout::align_of::<T>() },
8185
None => true,
8286
}
8387
}
8488

89+
pub proof fn tracked_borrow_points_to(tracked &self) -> (tracked ret: &PointsTo<T>)
90+
returns
91+
&self.points_to,
92+
{
93+
&self.points_to
94+
}
95+
96+
pub proof fn tracked_get_points_to(tracked self) -> (tracked ret: PointsTo<T>)
97+
returns
98+
self.points_to,
99+
{
100+
self.points_to
101+
}
102+
85103
pub proof fn new(
86104
tracked points_to: PointsTo<T>,
87105
tracked dealloc: Option<Dealloc>,

0 commit comments

Comments
 (0)