Skip to content

Commit 1e0af98

Browse files
Rollup merge of rust-lang#97027 - cuviper:yesalias-refcell, r=thomcc
Use pointers in `cell::{Ref,RefMut}` to avoid `noalias` When `Ref` and `RefMut` were based on references, they would get LLVM `noalias` attributes that were incorrect, because that alias guarantee is only true until the guard drops. A `&RefCell` on the same value can get a new borrow that aliases the previous guard, possibly leading to miscompilation. Using `NonNull` pointers in `Ref` and `RefCell` avoids `noalias`. Fixes the library side of rust-lang#63787, but we still might want to explore language solutions there.
2 parents 0620b2e + 15d8c00 commit 1e0af98

File tree

3 files changed

+98
-32
lines changed

3 files changed

+98
-32
lines changed

library/core/src/cell.rs

+48-32
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,10 @@
194194

195195
use crate::cmp::Ordering;
196196
use crate::fmt::{self, Debug, Display};
197-
use crate::marker::Unsize;
197+
use crate::marker::{PhantomData, Unsize};
198198
use crate::mem;
199199
use crate::ops::{CoerceUnsized, Deref, DerefMut};
200-
use crate::ptr;
200+
use crate::ptr::{self, NonNull};
201201

202202
/// A mutable memory location.
203203
///
@@ -896,7 +896,8 @@ impl<T: ?Sized> RefCell<T> {
896896

897897
// SAFETY: `BorrowRef` ensures that there is only immutable access
898898
// to the value while borrowed.
899-
Ok(Ref { value: unsafe { &*self.value.get() }, borrow: b })
899+
let value = unsafe { NonNull::new_unchecked(self.value.get()) };
900+
Ok(Ref { value, borrow: b })
900901
}
901902
None => Err(BorrowError {
902903
// If a borrow occurred, then we must already have an outstanding borrow,
@@ -980,8 +981,9 @@ impl<T: ?Sized> RefCell<T> {
980981
self.borrowed_at.set(Some(crate::panic::Location::caller()));
981982
}
982983

983-
// SAFETY: `BorrowRef` guarantees unique access.
984-
Ok(RefMut { value: unsafe { &mut *self.value.get() }, borrow: b })
984+
// SAFETY: `BorrowRefMut` guarantees unique access.
985+
let value = unsafe { NonNull::new_unchecked(self.value.get()) };
986+
Ok(RefMut { value, borrow: b, marker: PhantomData })
985987
}
986988
None => Err(BorrowMutError {
987989
// If a borrow occurred, then we must already have an outstanding borrow,
@@ -1314,7 +1316,9 @@ impl Clone for BorrowRef<'_> {
13141316
#[stable(feature = "rust1", since = "1.0.0")]
13151317
#[must_not_suspend = "holding a Ref across suspend points can cause BorrowErrors"]
13161318
pub struct Ref<'b, T: ?Sized + 'b> {
1317-
value: &'b T,
1319+
// NB: we use a pointer instead of `&'b T` to avoid `noalias` violations, because a
1320+
// `Ref` argument doesn't hold immutability for its whole scope, only until it drops.
1321+
value: NonNull<T>,
13181322
borrow: BorrowRef<'b>,
13191323
}
13201324

@@ -1324,7 +1328,8 @@ impl<T: ?Sized> Deref for Ref<'_, T> {
13241328

13251329
#[inline]
13261330
fn deref(&self) -> &T {
1327-
self.value
1331+
// SAFETY: the value is accessible as long as we hold our borrow.
1332+
unsafe { self.value.as_ref() }
13281333
}
13291334
}
13301335

@@ -1368,7 +1373,7 @@ impl<'b, T: ?Sized> Ref<'b, T> {
13681373
where
13691374
F: FnOnce(&T) -> &U,
13701375
{
1371-
Ref { value: f(orig.value), borrow: orig.borrow }
1376+
Ref { value: NonNull::from(f(&*orig)), borrow: orig.borrow }
13721377
}
13731378

13741379
/// Makes a new `Ref` for an optional component of the borrowed data. The
@@ -1399,8 +1404,8 @@ impl<'b, T: ?Sized> Ref<'b, T> {
13991404
where
14001405
F: FnOnce(&T) -> Option<&U>,
14011406
{
1402-
match f(orig.value) {
1403-
Some(value) => Ok(Ref { value, borrow: orig.borrow }),
1407+
match f(&*orig) {
1408+
Some(value) => Ok(Ref { value: NonNull::from(value), borrow: orig.borrow }),
14041409
None => Err(orig),
14051410
}
14061411
}
@@ -1431,9 +1436,12 @@ impl<'b, T: ?Sized> Ref<'b, T> {
14311436
where
14321437
F: FnOnce(&T) -> (&U, &V),
14331438
{
1434-
let (a, b) = f(orig.value);
1439+
let (a, b) = f(&*orig);
14351440
let borrow = orig.borrow.clone();
1436-
(Ref { value: a, borrow }, Ref { value: b, borrow: orig.borrow })
1441+
(
1442+
Ref { value: NonNull::from(a), borrow },
1443+
Ref { value: NonNull::from(b), borrow: orig.borrow },
1444+
)
14371445
}
14381446

14391447
/// Convert into a reference to the underlying data.
@@ -1467,7 +1475,8 @@ impl<'b, T: ?Sized> Ref<'b, T> {
14671475
// unique reference to the borrowed RefCell. No further mutable references can be created
14681476
// from the original cell.
14691477
mem::forget(orig.borrow);
1470-
orig.value
1478+
// SAFETY: after forgetting, we can form a reference for the rest of lifetime `'b`.
1479+
unsafe { orig.value.as_ref() }
14711480
}
14721481
}
14731482

@@ -1507,13 +1516,13 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
15071516
/// ```
15081517
#[stable(feature = "cell_map", since = "1.8.0")]
15091518
#[inline]
1510-
pub fn map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> RefMut<'b, U>
1519+
pub fn map<U: ?Sized, F>(mut orig: RefMut<'b, T>, f: F) -> RefMut<'b, U>
15111520
where
15121521
F: FnOnce(&mut T) -> &mut U,
15131522
{
15141523
// FIXME(nll-rfc#40): fix borrow-check
1515-
let RefMut { value, borrow } = orig;
1516-
RefMut { value: f(value), borrow }
1524+
let value = NonNull::from(f(&mut *orig));
1525+
RefMut { value, borrow: orig.borrow, marker: PhantomData }
15171526
}
15181527

15191528
/// Makes a new `RefMut` for an optional component of the borrowed data. The
@@ -1548,23 +1557,20 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
15481557
/// ```
15491558
#[unstable(feature = "cell_filter_map", reason = "recently added", issue = "81061")]
15501559
#[inline]
1551-
pub fn filter_map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> Result<RefMut<'b, U>, Self>
1560+
pub fn filter_map<U: ?Sized, F>(mut orig: RefMut<'b, T>, f: F) -> Result<RefMut<'b, U>, Self>
15521561
where
15531562
F: FnOnce(&mut T) -> Option<&mut U>,
15541563
{
15551564
// FIXME(nll-rfc#40): fix borrow-check
1556-
let RefMut { value, borrow } = orig;
1557-
let value = value as *mut T;
15581565
// SAFETY: function holds onto an exclusive reference for the duration
15591566
// of its call through `orig`, and the pointer is only de-referenced
15601567
// inside of the function call never allowing the exclusive reference to
15611568
// escape.
1562-
match f(unsafe { &mut *value }) {
1563-
Some(value) => Ok(RefMut { value, borrow }),
1564-
None => {
1565-
// SAFETY: same as above.
1566-
Err(RefMut { value: unsafe { &mut *value }, borrow })
1569+
match f(&mut *orig) {
1570+
Some(value) => {
1571+
Ok(RefMut { value: NonNull::from(value), borrow: orig.borrow, marker: PhantomData })
15671572
}
1573+
None => Err(orig),
15681574
}
15691575
}
15701576

@@ -1596,15 +1602,18 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
15961602
#[stable(feature = "refcell_map_split", since = "1.35.0")]
15971603
#[inline]
15981604
pub fn map_split<U: ?Sized, V: ?Sized, F>(
1599-
orig: RefMut<'b, T>,
1605+
mut orig: RefMut<'b, T>,
16001606
f: F,
16011607
) -> (RefMut<'b, U>, RefMut<'b, V>)
16021608
where
16031609
F: FnOnce(&mut T) -> (&mut U, &mut V),
16041610
{
1605-
let (a, b) = f(orig.value);
16061611
let borrow = orig.borrow.clone();
1607-
(RefMut { value: a, borrow }, RefMut { value: b, borrow: orig.borrow })
1612+
let (a, b) = f(&mut *orig);
1613+
(
1614+
RefMut { value: NonNull::from(a), borrow, marker: PhantomData },
1615+
RefMut { value: NonNull::from(b), borrow: orig.borrow, marker: PhantomData },
1616+
)
16081617
}
16091618

16101619
/// Convert into a mutable reference to the underlying data.
@@ -1630,14 +1639,15 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
16301639
/// assert!(cell.try_borrow_mut().is_err());
16311640
/// ```
16321641
#[unstable(feature = "cell_leak", issue = "69099")]
1633-
pub fn leak(orig: RefMut<'b, T>) -> &'b mut T {
1642+
pub fn leak(mut orig: RefMut<'b, T>) -> &'b mut T {
16341643
// By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell can't
16351644
// go back to UNUSED within the lifetime `'b`. Resetting the reference tracking state would
16361645
// require a unique reference to the borrowed RefCell. No further references can be created
16371646
// from the original cell within that lifetime, making the current borrow the only
16381647
// reference for the remaining lifetime.
16391648
mem::forget(orig.borrow);
1640-
orig.value
1649+
// SAFETY: after forgetting, we can form a reference for the rest of lifetime `'b`.
1650+
unsafe { orig.value.as_mut() }
16411651
}
16421652
}
16431653

@@ -1692,8 +1702,12 @@ impl<'b> BorrowRefMut<'b> {
16921702
#[stable(feature = "rust1", since = "1.0.0")]
16931703
#[must_not_suspend = "holding a RefMut across suspend points can cause BorrowErrors"]
16941704
pub struct RefMut<'b, T: ?Sized + 'b> {
1695-
value: &'b mut T,
1705+
// NB: we use a pointer instead of `&'b mut T` to avoid `noalias` violations, because a
1706+
// `RefMut` argument doesn't hold exclusivity for its whole scope, only until it drops.
1707+
value: NonNull<T>,
16961708
borrow: BorrowRefMut<'b>,
1709+
// NonNull is covariant over T, so we need to reintroduce invariance.
1710+
marker: PhantomData<&'b mut T>,
16971711
}
16981712

16991713
#[stable(feature = "rust1", since = "1.0.0")]
@@ -1702,15 +1716,17 @@ impl<T: ?Sized> Deref for RefMut<'_, T> {
17021716

17031717
#[inline]
17041718
fn deref(&self) -> &T {
1705-
self.value
1719+
// SAFETY: the value is accessible as long as we hold our borrow.
1720+
unsafe { self.value.as_ref() }
17061721
}
17071722
}
17081723

17091724
#[stable(feature = "rust1", since = "1.0.0")]
17101725
impl<T: ?Sized> DerefMut for RefMut<'_, T> {
17111726
#[inline]
17121727
fn deref_mut(&mut self) -> &mut T {
1713-
self.value
1728+
// SAFETY: the value is accessible as long as we hold our borrow.
1729+
unsafe { self.value.as_mut() }
17141730
}
17151731
}
17161732

src/test/codegen/noalias-refcell.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// compile-flags: -O -C no-prepopulate-passes -Z mutable-noalias=yes
2+
3+
#![crate_type = "lib"]
4+
5+
use std::cell::{Ref, RefCell, RefMut};
6+
7+
// Make sure that none of the arguments get a `noalias` attribute, because
8+
// the `RefCell` might alias writes after either `Ref`/`RefMut` is dropped.
9+
10+
// CHECK-LABEL: @maybe_aliased(
11+
// CHECK-NOT: noalias
12+
// CHECK-SAME: %_refcell
13+
#[no_mangle]
14+
pub unsafe fn maybe_aliased(_: Ref<'_, i32>, _: RefMut<'_, i32>, _refcell: &RefCell<i32>) {}

src/test/ui/issues/issue-63787.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// run-pass
2+
// compile-flags: -O
3+
4+
// Make sure that `Ref` and `RefMut` do not make false promises about aliasing,
5+
// because once they drop, their reference/pointer can alias other writes.
6+
7+
// Adapted from comex's proof of concept:
8+
// https://github.com/rust-lang/rust/issues/63787#issuecomment-523588164
9+
10+
use std::cell::RefCell;
11+
use std::ops::Deref;
12+
13+
pub fn break_if_r_is_noalias(rc: &RefCell<i32>, r: impl Deref<Target = i32>) -> i32 {
14+
let ptr1 = &*r as *const i32;
15+
let a = *r;
16+
drop(r);
17+
*rc.borrow_mut() = 2;
18+
let r2 = rc.borrow();
19+
let ptr2 = &*r2 as *const i32;
20+
if ptr2 != ptr1 {
21+
panic!();
22+
}
23+
// If LLVM knows the pointers are the same, and if `r` was `noalias`,
24+
// then it may replace this with `a + a`, ignoring the earlier write.
25+
a + *r2
26+
}
27+
28+
fn main() {
29+
let mut rc = RefCell::new(1);
30+
let res = break_if_r_is_noalias(&rc, rc.borrow());
31+
assert_eq!(res, 3);
32+
33+
*rc.get_mut() = 1;
34+
let res = break_if_r_is_noalias(&rc, rc.borrow_mut());
35+
assert_eq!(res, 3);
36+
}

0 commit comments

Comments
 (0)