Skip to content

Unsound issue in inner::drop #1

@CXWorks

Description

@CXWorks

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.

wgp/src/inner.rs

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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions