-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Hi, thanks for your time to read this issue.
Our static analyzer find a potential unsound issue in the drop of Inner, where it doesn't provide enough check to ensure the soundness.
Lines 71 to 87 in 90753e1
| fn drop(&mut self) { | |
| #[inline(never)] | |
| unsafe fn drop_slow(this: *mut Inner, old_refcount: usize) { | |
| match old_refcount { | |
| 2 => (*this).waker.wake(), | |
| 1 => drop(Box::from_raw(this)), | |
| _ => {} | |
| } | |
| } | |
| let old_refcount = self.deref().refcount.fetch_sub(1, Ordering::Release); | |
| if old_refcount > 2 { | |
| return; | |
| } | |
| self.deref().refcount.load(Ordering::Acquire); | |
| unsafe { drop_slow(self.0.as_ptr(), old_refcount) } | |
| } |
There is no synchronization that ensures only one thread proceeds to the drop(Box::from_raw(...)). The fetch_sub with Ordering::Release and the later load(Ordering::Acquire) do not form a full acquire–release synchronization that would ensure mutual exclusion or visibility of destruction.
A quick fix is turn the fetch_sub into acq-rel :
let old_refcount = self.deref().refcount.fetch_sub(1, Ordering::AcqRel);
if old_refcount == 1 {
unsafe { drop(Box::from_raw(self.0.as_ptr())) }
} else if old_refcount == 2 {
unsafe { (*self.0.as_ptr()).waker.wake(); }
}Thanks again for your time.
Nugine
Metadata
Metadata
Assignees
Labels
No labels