Skip to content

Commit fcbf43e

Browse files
committed
Replace use of references in Ref and RefMut with NonNull to
address stacked borrows UB detected by Miri. This is similar to bholley/atomic_refcell#18
1 parent bcbc2f2 commit fcbf43e

File tree

1 file changed

+70
-33
lines changed

1 file changed

+70
-33
lines changed

src/cell.rs

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Helper module for some internals, most users don't need to interact with it.
22
3+
use core::marker::PhantomData;
4+
use core::ptr::NonNull;
35
use std::{
46
cell::UnsafeCell,
57
error::Error,
@@ -42,7 +44,7 @@ impl Error for InvalidBorrow {
4244
#[derive(Debug)]
4345
pub struct Ref<'a, T: ?Sized + 'a> {
4446
flag: &'a AtomicUsize,
45-
value: &'a T,
47+
value: NonNull<T>,
4648
}
4749

4850
impl<'a, T: ?Sized> Ref<'a, T> {
@@ -95,7 +97,7 @@ impl<'a, T: ?Sized> Ref<'a, T> {
9597
{
9698
let val = Ref {
9799
flag: self.flag,
98-
value: f(self.value),
100+
value: NonNull::from(f(&*self)),
99101
};
100102

101103
std::mem::forget(self);
@@ -108,7 +110,8 @@ impl<'a, T: ?Sized> Deref for Ref<'a, T> {
108110
type Target = T;
109111

110112
fn deref(&self) -> &T {
111-
self.value
113+
// SAFETY: `Ref` holds a shared borrow of the value.
114+
unsafe { self.value.as_ref() }
112115
}
113116
}
114117

@@ -134,7 +137,10 @@ impl<'a, T: ?Sized> Clone for Ref<'a, T> {
134137
#[derive(Debug)]
135138
pub struct RefMut<'a, T: ?Sized + 'a> {
136139
flag: &'a AtomicUsize,
137-
value: &'a mut T,
140+
value: NonNull<T>,
141+
// `NonNull<T>` is covariant over `T` but we use it in the place of a mutable reference so we need to be
142+
// invariant over `T`.
143+
marker: PhantomData<&'a mut T>,
138144
}
139145

140146
impl<'a, T: ?Sized> RefMut<'a, T> {
@@ -182,7 +188,7 @@ impl<'a, T: ?Sized> RefMut<'a, T> {
182188
/// let b2: RefMut<'_, u32> = RefMut::map(b1, |t| &mut t.0);
183189
/// assert_eq!(*b2, 5);
184190
/// ```
185-
pub fn map<U, F>(self, f: F) -> RefMut<'a, U>
191+
pub fn map<U, F>(mut self, f: F) -> RefMut<'a, U>
186192
where
187193
F: FnOnce(&mut T) -> &mut U,
188194
U: ?Sized,
@@ -192,7 +198,7 @@ impl<'a, T: ?Sized> RefMut<'a, T> {
192198
// the given `RefMut`, the lifetime we created through turning the
193199
// pointer into a ref is valid.
194200
let flag = self.flag;
195-
let value = unsafe { &mut *(self.value as *mut _) };
201+
let value = NonNull::from(f(&mut *self));
196202

197203
// We have to forget self so that we do not run `Drop`. Further it's safe
198204
// because we are creating a new `RefMut`, with the same flag, which
@@ -201,7 +207,8 @@ impl<'a, T: ?Sized> RefMut<'a, T> {
201207

202208
RefMut {
203209
flag,
204-
value: f(value),
210+
value,
211+
marker: PhantomData,
205212
}
206213
}
207214
}
@@ -210,13 +217,17 @@ impl<'a, T: ?Sized> Deref for RefMut<'a, T> {
210217
type Target = T;
211218

212219
fn deref(&self) -> &T {
213-
self.value
220+
// SAFETY: `RefMut` holds an exclusive borrow of the value and we have a shared borrow of
221+
// `RefMut` here.
222+
unsafe { self.value.as_ref() }
214223
}
215224
}
216225

217226
impl<'a, T: ?Sized> DerefMut for RefMut<'a, T> {
218227
fn deref_mut(&mut self) -> &mut T {
219-
self.value
228+
// SAFETY: `RefMut` holds an exclusive borrow of the value and we have an exclusive borrow
229+
// of `RefMut` here.
230+
unsafe { self.value.as_mut() }
220231
}
221232
}
222233

@@ -261,7 +272,7 @@ impl<T> TrustCell<T> {
261272

262273
Ref {
263274
flag: &self.flag,
264-
value: unsafe { &*self.inner.get() },
275+
value: unsafe { NonNull::new_unchecked(self.inner.get()) },
265276
}
266277
}
267278

@@ -274,7 +285,7 @@ impl<T> TrustCell<T> {
274285

275286
Ok(Ref {
276287
flag: &self.flag,
277-
value: unsafe { &*self.inner.get() },
288+
value: unsafe { NonNull::new_unchecked(self.inner.get()) },
278289
})
279290
}
280291

@@ -292,7 +303,8 @@ impl<T> TrustCell<T> {
292303

293304
RefMut {
294305
flag: &self.flag,
295-
value: unsafe { &mut *self.inner.get() },
306+
value: unsafe { NonNull::new_unchecked(self.inner.get()) },
307+
marker: PhantomData,
296308
}
297309
}
298310

@@ -305,7 +317,8 @@ impl<T> TrustCell<T> {
305317

306318
Ok(RefMut {
307319
flag: &self.flag,
308-
value: unsafe { &mut *self.inner.get() },
320+
value: unsafe { NonNull::new_unchecked(self.inner.get()) },
321+
marker: PhantomData,
309322
})
310323
}
311324

@@ -471,22 +484,27 @@ mod tests {
471484
assert!(cell.try_borrow_mut().is_err());
472485
}
473486

487+
fn make_ref<'a, T: ?Sized>(flag: &'a AtomicUsize, value: &'a T) -> Ref<'a, T> {
488+
Ref {
489+
flag,
490+
value: NonNull::from(value),
491+
}
492+
}
493+
474494
#[test]
475495
fn ref_with_non_sized() {
476-
let r: Ref<'_, [i32]> = Ref {
477-
flag: &AtomicUsize::new(1),
478-
value: &[2, 3, 4, 5][..],
479-
};
496+
let value = &[2, 3, 4, 5][..];
497+
let flag = AtomicUsize::new(1);
498+
let r: Ref<'_, [i32]> = make_ref(&flag, value);
480499

481500
assert_eq!(&*r, &[2, 3, 4, 5][..]);
482501
}
483502

484503
#[test]
485504
fn ref_with_non_sized_clone() {
486-
let r: Ref<'_, [i32]> = Ref {
487-
flag: &AtomicUsize::new(1),
488-
value: &[2, 3, 4, 5][..],
489-
};
505+
let value = &[2, 3, 4, 5][..];
506+
let flag = AtomicUsize::new(1);
507+
let r: Ref<'_, [i32]> = make_ref(&flag, value);
490508
let rr = r.clone();
491509

492510
assert_eq!(&*r, &[2, 3, 4, 5][..]);
@@ -498,30 +516,35 @@ mod tests {
498516

499517
#[test]
500518
fn ref_with_trait_obj() {
501-
let ra: Ref<'_, dyn std::any::Any> = Ref {
502-
flag: &AtomicUsize::new(1),
503-
value: &2i32,
504-
};
519+
let value = &2i32;
520+
let flag = AtomicUsize::new(1);
521+
let ra: Ref<'_, dyn std::any::Any> = make_ref(&flag, value);
505522

506523
assert_eq!(ra.downcast_ref::<i32>().unwrap(), &2i32);
507524
}
508525

526+
fn make_ref_mut<'a, T: ?Sized>(flag: &'a AtomicUsize, value: &'a mut T) -> RefMut<'a, T> {
527+
RefMut {
528+
flag,
529+
value: NonNull::from(value),
530+
marker: PhantomData,
531+
}
532+
}
533+
509534
#[test]
510535
fn ref_mut_with_non_sized() {
511-
let mut r: RefMut<'_, [i32]> = RefMut {
512-
flag: &AtomicUsize::new(1),
513-
value: &mut [2, 3, 4, 5][..],
514-
};
536+
let value: &mut [i32] = &mut [2, 3, 4, 5][..];
537+
let flag = AtomicUsize::new(1);
538+
let mut r: RefMut<'_, [i32]> = make_ref_mut(&flag, value);
515539

516540
assert_eq!(&mut *r, &mut [2, 3, 4, 5][..]);
517541
}
518542

519543
#[test]
520544
fn ref_mut_with_trait_obj() {
521-
let mut ra: RefMut<'_, dyn std::any::Any> = RefMut {
522-
flag: &AtomicUsize::new(1),
523-
value: &mut 2i32,
524-
};
545+
let value = &mut 2i32;
546+
let flag = AtomicUsize::new(1);
547+
let mut ra: RefMut<'_, dyn std::any::Any> = make_ref_mut(&flag, value);
525548

526549
assert_eq!(ra.downcast_mut::<i32>().unwrap(), &mut 2i32);
527550
}
@@ -613,4 +636,18 @@ mod tests {
613636
drop(r);
614637
assert_eq!(cell.flag.load(Ordering::SeqCst), 0);
615638
}
639+
640+
// Test intended to allow Miri to catch bugs like this scenario:
641+
// https://github.com/rust-lang/rust/issues/63787
642+
#[test]
643+
fn drop_and_borrow_in_fn_call() {
644+
fn drop_and_borrow(cell: &TrustCell<u8>, borrow: Ref<'_, u8>) {
645+
drop(borrow);
646+
*cell.borrow_mut() = 7u8;
647+
}
648+
649+
let a = TrustCell::new(4u8);
650+
let borrow = a.borrow();
651+
drop_and_borrow(&a, borrow);
652+
}
616653
}

0 commit comments

Comments
 (0)