Skip to content

Commit ee6e705

Browse files
committed
Fix UB in Arc
Use raw pointers to avoid making any assertions about the data field.
1 parent ed084b0 commit ee6e705

File tree

1 file changed

+26
-9
lines changed

1 file changed

+26
-9
lines changed

src/liballoc/sync.rs

+26-9
Original file line numberDiff line numberDiff line change
@@ -866,12 +866,10 @@ impl<T: ?Sized> Arc<T> {
866866
unsafe fn drop_slow(&mut self) {
867867
// Destroy the data at this time, even though we may not free the box
868868
// allocation itself (there may still be weak pointers lying around).
869-
ptr::drop_in_place(&mut self.ptr.as_mut().data);
869+
ptr::drop_in_place(Self::get_mut_unchecked(self));
870870

871-
if self.inner().weak.fetch_sub(1, Release) == 1 {
872-
acquire!(self.inner().weak);
873-
Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()))
874-
}
871+
// Drop the weak ref collectively held by all strong references
872+
drop(Weak { ptr: self.ptr });
875873
}
876874

877875
#[inline]
@@ -1201,7 +1199,7 @@ impl<T: Clone> Arc<T> {
12011199

12021200
// As with `get_mut()`, the unsafety is ok because our reference was
12031201
// either unique to begin with, or became one upon cloning the contents.
1204-
unsafe { &mut this.ptr.as_mut().data }
1202+
unsafe { Self::get_mut_unchecked(this) }
12051203
}
12061204
}
12071205

@@ -1277,7 +1275,9 @@ impl<T: ?Sized> Arc<T> {
12771275
#[inline]
12781276
#[unstable(feature = "get_mut_unchecked", issue = "63292")]
12791277
pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {
1280-
&mut this.ptr.as_mut().data
1278+
// We are careful to *not* create a reference covering the "count" fields, as
1279+
// this would alias with concurrent access to the reference counts (e.g. by `Weak`).
1280+
&mut (*this.ptr.as_ptr()).data
12811281
}
12821282

12831283
/// Determine whether this is the unique reference (including weak refs) to
@@ -1568,6 +1568,13 @@ impl<T> Weak<T> {
15681568
}
15691569
}
15701570

1571+
/// Helper type to allow accessing the reference counts without
1572+
/// making any assertions about the data field.
1573+
struct WeakInner<'a> {
1574+
weak: &'a atomic::AtomicUsize,
1575+
strong: &'a atomic::AtomicUsize,
1576+
}
1577+
15711578
impl<T: ?Sized> Weak<T> {
15721579
/// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying
15731580
/// dropping of the inner value if successful.
@@ -1673,8 +1680,18 @@ impl<T: ?Sized> Weak<T> {
16731680
/// Returns `None` when the pointer is dangling and there is no allocated `ArcInner`,
16741681
/// (i.e., when this `Weak` was created by `Weak::new`).
16751682
#[inline]
1676-
fn inner(&self) -> Option<&ArcInner<T>> {
1677-
if is_dangling(self.ptr) { None } else { Some(unsafe { self.ptr.as_ref() }) }
1683+
fn inner(&self) -> Option<WeakInner<'_>> {
1684+
if is_dangling(self.ptr) {
1685+
None
1686+
} else {
1687+
// We are careful to *not* create a reference covering the "data" field, as
1688+
// the field may be mutated concurrently (for example, if the last `Arc`
1689+
// is dropped, the data field will be dropped in-place).
1690+
Some(unsafe {
1691+
let ptr = self.ptr.as_ptr();
1692+
WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak }
1693+
})
1694+
}
16781695
}
16791696

16801697
/// Returns `true` if the two `Weak`s point to the same allocation (similar to

0 commit comments

Comments
 (0)