Closed
Description
unsafe fn try_register_dtor(&self) -> bool {
match self.dtor_state.get() {
DtorState::Unregistered => {
// SAFETY: dtor registration happens before initialization.
// Passing `self` as a pointer while using `destroy_value<T>`
// is safe because the function will build a pointer to a
// Key<T>, which is the type of self and so find the correct
// size.
unsafe { register_dtor(self as *const _ as *mut u8, destroy_value::<T>) }; // #1
self.dtor_state.set(DtorState::Registered);
true
}
DtorState::Registered => {
// recursively initialized
true
}
DtorState::RunningOrHasRun => false,
}
}
At #1
, acquire a pointer of type * mut u8
from a shared reference &Key<T>
, and subsequently, in the destroy_value
, we reinterpret * mut u8
as * mut Key<T>
and use it as a mutable pointer
unsafe extern "C" fn destroy_value<T>(ptr: *mut u8) {
let ptr = ptr as *mut Key<T>;
// SAFETY:
//
// The pointer `ptr` has been built just above and comes from
// `try_register_dtor` where it is originally a Key<T> coming from `self`,
// making it non-NUL and of the correct type.
//
// Right before we run the user destructor be sure to set the
// `Option<T>` to `None`, and `dtor_state` to `RunningOrHasRun`. This
// causes future calls to `get` to run `try_initialize_drop` again,
// which will now fail, and return `None`.
//
// Wrap the call in a catch to ensure unwinding is caught in the event
// a panic takes place in a destructor.
if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| unsafe {
let value = (*ptr).inner.take(); // #2
(*ptr).dtor_state.set(DtorState::RunningOrHasRun);
drop(value);
})) {
rtabort!("thread local panicked on drop");
}
}
At #2
the method take
of the field inner
requires & mut self
as defined in
The whole process can be that we use &Key<T>
to acquire a & mut Key<T>
and call a mutable method on that variable, which seems problematic.