Skip to content

Commit 841f798

Browse files
committed
Detect simultaneous borrow and drop of UnsafeCell
Dropping an UnsafeCell or calling into_inner can be seen as mutable access, which is not allowed to happen while another borrow exists. The downside of this change is the added Drop implementation of UnsafeCell. This restricts some uses of UnsafeCell that were previously allowed related to drop check. I went with an approach using unsafe. It is possible to implement this safely but it has too much overhead. We would have to wrap the data field with Option to allow safely taking. This is fine but we would also need to Box it because the type parameter T is ?Sized, which cannot be put into Option. fixes #349
1 parent a7033ee commit 841f798

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

src/cell/unsafe_cell.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::mem::ManuallyDrop;
2+
13
use crate::rt;
24

35
/// A checked version of `std::cell::UnsafeCell`.
@@ -115,7 +117,18 @@ impl<T> UnsafeCell<T> {
115117

116118
/// Unwraps the value.
117119
pub fn into_inner(self) -> T {
118-
self.data.into_inner()
120+
// Observe that we have mutable access.
121+
self.get_mut();
122+
123+
// We would like to destructure self. This is not possible because of
124+
// the Drop implementation. Work around it using unsafe. Note that we
125+
// extract `self.state` even though we don't use it. This prevents it
126+
// from leaking.
127+
let self_ = ManuallyDrop::new(self);
128+
let _state = unsafe { std::ptr::read(&self_.state) };
129+
let data = unsafe { std::ptr::read(&self_.data) };
130+
131+
data.into_inner()
119132
}
120133
}
121134

@@ -214,6 +227,13 @@ impl<T> From<T> for UnsafeCell<T> {
214227
}
215228
}
216229

230+
impl<T: ?Sized> Drop for UnsafeCell<T> {
231+
fn drop(&mut self) {
232+
// Observe that we have mutable access.
233+
self.get_mut();
234+
}
235+
}
236+
217237
impl<T: ?Sized> ConstPtr<T> {
218238
/// Dereference the raw pointer.
219239
///

tests/unsafe_cell.rs

+25
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@ fn atomic_causality_success() {
6060
});
6161
}
6262

63+
// This test is ignored because it causes an abort because of a double panic.
64+
// More discussion at https://github.com/tokio-rs/loom/issues/350 .
6365
#[test]
6466
#[should_panic]
67+
#[ignore]
6568
fn atomic_causality_fail() {
6669
struct Chan {
6770
data: UnsafeCell<usize>,
@@ -333,3 +336,25 @@ fn unsafe_cell_access_after_sync() {
333336
}
334337
});
335338
}
339+
340+
#[test]
341+
#[should_panic]
342+
fn unsafe_cell_access_during_drop() {
343+
loom::model(|| {
344+
let x = UnsafeCell::new(());
345+
let _guard = x.get();
346+
drop(x);
347+
});
348+
}
349+
350+
// Unfortunately we cannot repeat the previous test with `into_inner` instead of
351+
// Drop because it leads to a second panic when UnsafeCell::drop runs and guard
352+
// is still alive. The second panic leads to an abort.
353+
354+
#[test]
355+
fn unsafe_cell_into_inner_ok() {
356+
loom::model(|| {
357+
let x = UnsafeCell::new(0usize);
358+
x.into_inner();
359+
});
360+
}

0 commit comments

Comments
 (0)