Disallow missing unsafe blocks in unsafe fn in panicking.rs

This adds SAFETY comments where necessary, explaining the preconditions
and how they are respected.
This commit is contained in:
Alexis Bourget 2020-07-29 21:58:09 +02:00
parent 2c28244cf0
commit 82ccdab96c
2 changed files with 49 additions and 7 deletions

View File

@ -7,6 +7,8 @@
//! * Executing a panic up to doing the actual implementation
//! * Shims around "try"
#![deny(unsafe_op_in_unsafe_fn)]
use core::panic::{BoxMeUp, Location, PanicInfo};
use crate::any::Any;
@ -322,11 +324,22 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
let mut data = Data { f: ManuallyDrop::new(f) };
let data_ptr = &mut data as *mut _ as *mut u8;
return if intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
Ok(ManuallyDrop::into_inner(data.r))
} else {
Err(ManuallyDrop::into_inner(data.p))
};
// SAFETY:
//
// Access to the union's fields: this is `std` and we know that the `r#try`
// intrinsic fills in the `r` or `p` union field based on its return value.
//
// The call to `intrinsics::r#try` is made safe by:
// - `do_call`, the first argument, can be called with the initial `data_ptr`.
// - `do_catch`, the second argument, can be called with the `data_ptr` as well.
// See their safety preconditions for more informations
unsafe {
return if intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
Ok(ManuallyDrop::into_inner(data.r))
} else {
Err(ManuallyDrop::into_inner(data.p))
};
}
// We consider unwinding to be rare, so mark this function as cold. However,
// do not mark it no-inline -- that decision is best to leave to the
@ -334,13 +347,25 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
// non-cold function, though, as of the writing of this comment).
#[cold]
unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
let obj = Box::from_raw(__rust_panic_cleanup(payload));
// SAFETY: The whole unsafe block hinges on a correct implementation of
// the panic handler `__rust_panic_cleanup`. As such we can only
// assume it returns the correct thing for `Box::from_raw` to work
// without undefined behavior.
let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) };
panic_count::decrease();
obj
}
// SAFETY:
// data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
// Its must contains a valid `f` (type: F) value that can be use to fill
// `data.r`.
//
// This function cannot be marked as `unsafe` because `intrinsics::r#try`
// expects normal function pointers.
#[inline]
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
// SAFETY: this is the responsibilty of the caller, see above.
unsafe {
let data = data as *mut Data<F, R>;
let data = &mut (*data);
@ -352,8 +377,21 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
// We *do* want this part of the catch to be inlined: this allows the
// compiler to properly track accesses to the Data union and optimize it
// away most of the time.
//
// SAFETY:
// data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
// Since this uses `cleanup` it also hinges on a correct implementation of
// `__rustc_panic_cleanup`.
//
// This function cannot be marked as `unsafe` because `intrinsics::r#try`
// expects normal function pointers.
#[inline]
fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
// SAFETY: this is the responsibilty of the caller, see above.
//
// When `__rustc_panic_cleaner` is correctly implemented we can rely
// on `obj` being the correct thing to pass to `data.p` (after wrapping
// in `ManuallyDrop`).
unsafe {
let data = data as *mut Data<F, R>;
let data = &mut (*data);

View File

@ -172,7 +172,11 @@ macro_rules! __thread_local_inner {
static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
$crate::thread::__OsLocalKeyInner::new();
__KEY.get(__init)
// FIXME: remove the #[allow(...)] marker when macros don't
// raise warning for missing/extraneous unsafe blocks anymore.
// See https://github.com/rust-lang/rust/issues/74838.
#[allow(unused_unsafe)]
unsafe { __KEY.get(__init) }
}
unsafe {