Skip to content

Commit 4d6992b

Browse files
committed
Auto 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 a09d36d + 1c3921f commit 4d6992b

File tree

4 files changed

+103
-38
lines changed

4 files changed

+103
-38
lines changed

library/core/src/cell.rs

+49-34
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,10 @@ 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+
// `NonNull` is also covariant over `T`, just like we would have with `&T`.
1322+
value: NonNull<T>,
13181323
borrow: BorrowRef<'b>,
13191324
}
13201325

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

13251330
#[inline]
13261331
fn deref(&self) -> &T {
1327-
self.value
1332+
// SAFETY: the value is accessible as long as we hold our borrow.
1333+
unsafe { self.value.as_ref() }
13281334
}
13291335
}
13301336

@@ -1368,7 +1374,7 @@ impl<'b, T: ?Sized> Ref<'b, T> {
13681374
where
13691375
F: FnOnce(&T) -> &U,
13701376
{
1371-
Ref { value: f(orig.value), borrow: orig.borrow }
1377+
Ref { value: NonNull::from(f(&*orig)), borrow: orig.borrow }
13721378
}
13731379

13741380
/// Makes a new `Ref` for an optional component of the borrowed data. The
@@ -1399,8 +1405,8 @@ impl<'b, T: ?Sized> Ref<'b, T> {
13991405
where
14001406
F: FnOnce(&T) -> Option<&U>,
14011407
{
1402-
match f(orig.value) {
1403-
Some(value) => Ok(Ref { value, borrow: orig.borrow }),
1408+
match f(&*orig) {
1409+
Some(value) => Ok(Ref { value: NonNull::from(value), borrow: orig.borrow }),
14041410
None => Err(orig),
14051411
}
14061412
}
@@ -1431,9 +1437,12 @@ impl<'b, T: ?Sized> Ref<'b, T> {
14311437
where
14321438
F: FnOnce(&T) -> (&U, &V),
14331439
{
1434-
let (a, b) = f(orig.value);
1440+
let (a, b) = f(&*orig);
14351441
let borrow = orig.borrow.clone();
1436-
(Ref { value: a, borrow }, Ref { value: b, borrow: orig.borrow })
1442+
(
1443+
Ref { value: NonNull::from(a), borrow },
1444+
Ref { value: NonNull::from(b), borrow: orig.borrow },
1445+
)
14371446
}
14381447

14391448
/// Convert into a reference to the underlying data.
@@ -1467,7 +1476,8 @@ impl<'b, T: ?Sized> Ref<'b, T> {
14671476
// unique reference to the borrowed RefCell. No further mutable references can be created
14681477
// from the original cell.
14691478
mem::forget(orig.borrow);
1470-
orig.value
1479+
// SAFETY: after forgetting, we can form a reference for the rest of lifetime `'b`.
1480+
unsafe { orig.value.as_ref() }
14711481
}
14721482
}
14731483

@@ -1507,13 +1517,12 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
15071517
/// ```
15081518
#[stable(feature = "cell_map", since = "1.8.0")]
15091519
#[inline]
1510-
pub fn map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> RefMut<'b, U>
1520+
pub fn map<U: ?Sized, F>(mut orig: RefMut<'b, T>, f: F) -> RefMut<'b, U>
15111521
where
15121522
F: FnOnce(&mut T) -> &mut U,
15131523
{
1514-
// 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,19 @@ 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
{
1555-
// FIXME(nll-rfc#40): fix borrow-check
1556-
let RefMut { value, borrow } = orig;
1557-
let value = value as *mut T;
15581564
// SAFETY: function holds onto an exclusive reference for the duration
15591565
// of its call through `orig`, and the pointer is only de-referenced
15601566
// inside of the function call never allowing the exclusive reference to
15611567
// 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 })
1568+
match f(&mut *orig) {
1569+
Some(value) => {
1570+
Ok(RefMut { value: NonNull::from(value), borrow: orig.borrow, marker: PhantomData })
15671571
}
1572+
None => Err(orig),
15681573
}
15691574
}
15701575

@@ -1596,15 +1601,18 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
15961601
#[stable(feature = "refcell_map_split", since = "1.35.0")]
15971602
#[inline]
15981603
pub fn map_split<U: ?Sized, V: ?Sized, F>(
1599-
orig: RefMut<'b, T>,
1604+
mut orig: RefMut<'b, T>,
16001605
f: F,
16011606
) -> (RefMut<'b, U>, RefMut<'b, V>)
16021607
where
16031608
F: FnOnce(&mut T) -> (&mut U, &mut V),
16041609
{
1605-
let (a, b) = f(orig.value);
16061610
let borrow = orig.borrow.clone();
1607-
(RefMut { value: a, borrow }, RefMut { value: b, borrow: orig.borrow })
1611+
let (a, b) = f(&mut *orig);
1612+
(
1613+
RefMut { value: NonNull::from(a), borrow, marker: PhantomData },
1614+
RefMut { value: NonNull::from(b), borrow: orig.borrow, marker: PhantomData },
1615+
)
16081616
}
16091617

16101618
/// Convert into a mutable reference to the underlying data.
@@ -1630,14 +1638,15 @@ impl<'b, T: ?Sized> RefMut<'b, T> {
16301638
/// assert!(cell.try_borrow_mut().is_err());
16311639
/// ```
16321640
#[unstable(feature = "cell_leak", issue = "69099")]
1633-
pub fn leak(orig: RefMut<'b, T>) -> &'b mut T {
1641+
pub fn leak(mut orig: RefMut<'b, T>) -> &'b mut T {
16341642
// By forgetting this BorrowRefMut we ensure that the borrow counter in the RefCell can't
16351643
// go back to UNUSED within the lifetime `'b`. Resetting the reference tracking state would
16361644
// require a unique reference to the borrowed RefCell. No further references can be created
16371645
// from the original cell within that lifetime, making the current borrow the only
16381646
// reference for the remaining lifetime.
16391647
mem::forget(orig.borrow);
1640-
orig.value
1648+
// SAFETY: after forgetting, we can form a reference for the rest of lifetime `'b`.
1649+
unsafe { orig.value.as_mut() }
16411650
}
16421651
}
16431652

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

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

17031716
#[inline]
17041717
fn deref(&self) -> &T {
1705-
self.value
1718+
// SAFETY: the value is accessible as long as we hold our borrow.
1719+
unsafe { self.value.as_ref() }
17061720
}
17071721
}
17081722

17091723
#[stable(feature = "rust1", since = "1.0.0")]
17101724
impl<T: ?Sized> DerefMut for RefMut<'_, T> {
17111725
#[inline]
17121726
fn deref_mut(&mut self) -> &mut T {
1713-
self.value
1727+
// SAFETY: the value is accessible as long as we hold our borrow.
1728+
unsafe { self.value.as_mut() }
17141729
}
17151730
}
17161731

src/etc/natvis/libcore.natvis

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
</Expand>
88
</Type>
99
<Type Name="core::cell::Ref&lt;*&gt;">
10-
<DisplayString>{value}</DisplayString>
10+
<DisplayString>{value.pointer}</DisplayString>
1111
<Expand>
12-
<ExpandedItem>value</ExpandedItem>
12+
<ExpandedItem>value.pointer</ExpandedItem>
1313
</Expand>
1414
</Type>
1515
<Type Name="core::cell::RefMut&lt;*&gt;">
16-
<DisplayString>{value}</DisplayString>
16+
<DisplayString>{value.pointer}</DisplayString>
1717
<Expand>
18-
<ExpandedItem>value</ExpandedItem>
18+
<ExpandedItem>value.pointer</ExpandedItem>
1919
</Expand>
2020
</Type>
2121
<Type Name="core::cell::RefCell&lt;*&gt;">

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)