Skip to content

Commit 5f36dc2

Browse files
committed
Explicitly pass strong ref as raw pointer to prevent UB in Arc::drop
1 parent c61b92b commit 5f36dc2

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

src/liballoc/sync.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ impl<T: ?Sized> Arc<T> {
552552
// allocation itself (there may still be weak pointers lying around).
553553
ptr::drop_in_place(&mut self.ptr.as_mut().data);
554554

555-
if self.inner().weak.fetch_sub(1, Release) == 1 {
555+
if atomic::AtomicUsize::fetch_sub_explicit(&self.inner().weak, 1, Release) == 1 {
556556
atomic::fence(Acquire);
557557
Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()))
558558
}
@@ -970,10 +970,11 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc<T> {
970970
/// [`Weak`]: ../../std/sync/struct.Weak.html
971971
#[inline]
972972
fn drop(&mut self) {
973-
// Because `fetch_sub` is already atomic, we do not need to synchronize
973+
// Because `fetch_sub_explicit` is already atomic, we do not need to synchronize
974974
// with other threads unless we are going to delete the object. This
975975
// same logic applies to the below `fetch_sub` to the `weak` count.
976-
if self.inner().strong.fetch_sub(1, Release) != 1 {
976+
// For preventing dangling self over the unsafe block strong ref pointer passed.
977+
if atomic::AtomicUsize::fetch_sub_explicit(&self.inner().strong, 1, Release) != 1 {
977978
return;
978979
}
979980

@@ -1350,7 +1351,7 @@ impl<T: ?Sized> Drop for Weak<T> {
13501351
return
13511352
};
13521353

1353-
if inner.weak.fetch_sub(1, Release) == 1 {
1354+
if atomic::AtomicUsize::fetch_sub_explicit(&inner.weak, 1, Release) == 1 {
13541355
atomic::fence(Acquire);
13551356
unsafe {
13561357
Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()))

src/libcore/sync/atomic.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,41 @@ assert_eq!(foo.load(Ordering::SeqCst), 10);
15751575
}
15761576
}
15771577

1578+
doc_comment! {
1579+
concat!("Subtracts from the given atomic value, returning the previous value.
1580+
1581+
This operation wraps around on overflow.
1582+
1583+
`fetch_sub_explicit` takes ", stringify!($atomic_type), " which will be substracted by given value
1584+
in respect to [`Ordering`] argument which describes the memory ordering
1585+
of this operation. All ordering modes are possible. Note that using
1586+
[`Acquire`] makes the store part of this operation [`Relaxed`], and
1587+
using [`Release`] makes the load part [`Relaxed`].
1588+
1589+
[`Ordering`]: enum.Ordering.html
1590+
[`Relaxed`]: enum.Ordering.html#variant.Relaxed
1591+
[`Release`]: enum.Ordering.html#variant.Release
1592+
[`Acquire`]: enum.Ordering.html#variant.Acquire
1593+
1594+
# Examples
1595+
1596+
```
1597+
", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
1598+
1599+
let foo = ", stringify!($atomic_type), "::new(20);
1600+
assert_eq!(", stringify!($atomic_type), ".fetch_sub_explicit(foo, Ordering::SeqCst), 20);
1601+
assert_eq!(foo.load(Ordering::SeqCst), 10);
1602+
```"),
1603+
#[inline]
1604+
#[$stable]
1605+
#[cfg(target_has_atomic = "cas")]
1606+
pub fn fetch_sub_explicit(f: *const $atomic_type,
1607+
val: $int_type,
1608+
order: Ordering) -> $int_type {
1609+
unsafe { atomic_sub((*f).v.get(), val, order) }
1610+
}
1611+
}
1612+
15781613
doc_comment! {
15791614
concat!("Bitwise \"and\" with the current value.
15801615

0 commit comments

Comments
 (0)