Skip to content

Commit b409137

Browse files
Imberflurrichard-uk1
authored andcommitted
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 7d4bc98 commit b409137

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)