Skip to content

Commit 8f95dc8

Browse files
authored
Rollup merge of #72533 - Diggsey:db-fix-arc-ub2, r=dtolnay
Resolve UB in Arc/Weak interaction (2) Use raw pointers to avoid making any assertions about the data field. Follow up from #72479, see that PR for more detail on the motivation. @RalfJung I was able to avoid a lot of the changes to `Weak`, by making a helper type (`WeakInner`) - because of auto-deref and because the fields have the same name, the rest of the code continues to compile.
2 parents cbe7b90 + ee6e705 commit 8f95dc8

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
@@ -867,12 +867,10 @@ impl<T: ?Sized> Arc<T> {
867867
unsafe fn drop_slow(&mut self) {
868868
// Destroy the data at this time, even though we may not free the box
869869
// allocation itself (there may still be weak pointers lying around).
870-
ptr::drop_in_place(&mut self.ptr.as_mut().data);
870+
ptr::drop_in_place(Self::get_mut_unchecked(self));
871871

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

878876
#[inline]
@@ -1204,7 +1202,7 @@ impl<T: Clone> Arc<T> {
12041202

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

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

12861286
/// Determine whether this is the unique reference (including weak refs) to
@@ -1571,6 +1571,13 @@ impl<T> Weak<T> {
15711571
}
15721572
}
15731573

1574+
/// Helper type to allow accessing the reference counts without
1575+
/// making any assertions about the data field.
1576+
struct WeakInner<'a> {
1577+
weak: &'a atomic::AtomicUsize,
1578+
strong: &'a atomic::AtomicUsize,
1579+
}
1580+
15741581
impl<T: ?Sized> Weak<T> {
15751582
/// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying
15761583
/// dropping of the inner value if successful.
@@ -1678,8 +1685,18 @@ impl<T: ?Sized> Weak<T> {
16781685
/// Returns `None` when the pointer is dangling and there is no allocated `ArcInner`,
16791686
/// (i.e., when this `Weak` was created by `Weak::new`).
16801687
#[inline]
1681-
fn inner(&self) -> Option<&ArcInner<T>> {
1682-
if is_dangling(self.ptr) { None } else { Some(unsafe { self.ptr.as_ref() }) }
1688+
fn inner(&self) -> Option<WeakInner<'_>> {
1689+
if is_dangling(self.ptr) {
1690+
None
1691+
} else {
1692+
// We are careful to *not* create a reference covering the "data" field, as
1693+
// the field may be mutated concurrently (for example, if the last `Arc`
1694+
// is dropped, the data field will be dropped in-place).
1695+
Some(unsafe {
1696+
let ptr = self.ptr.as_ptr();
1697+
WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak }
1698+
})
1699+
}
16831700
}
16841701

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

0 commit comments

Comments
 (0)