Add MIR validation for unwind out from nounwind functions

This commit is contained in:
Gary Guo 2023-06-28 15:15:34 +01:00
parent cec8e09edf
commit 56b933763e

View File

@ -18,6 +18,7 @@ use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_mir_dataflow::{Analysis, ResultsCursor}; use rustc_mir_dataflow::{Analysis, ResultsCursor};
use rustc_target::abi::{Size, FIRST_VARIANT}; use rustc_target::abi::{Size, FIRST_VARIANT};
use rustc_target::spec::abi::Abi;
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum EdgeKind { enum EdgeKind {
@ -58,6 +59,25 @@ impl<'tcx> MirPass<'tcx> for Validator {
.iterate_to_fixpoint() .iterate_to_fixpoint()
.into_results_cursor(body); .into_results_cursor(body);
let can_unwind = if mir_phase <= MirPhase::Runtime(RuntimePhase::Initial) {
// In this case `AbortUnwindingCalls` haven't yet been executed.
true
} else if !tcx.def_kind(def_id).is_fn_like() {
true
} else {
let body_ty = tcx.type_of(def_id).skip_binder();
let body_abi = match body_ty.kind() {
ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
ty::Closure(..) => Abi::RustCall,
ty::Generator(..) => Abi::Rust,
_ => {
span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase)
}
};
ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi)
};
let mut cfg_checker = CfgChecker { let mut cfg_checker = CfgChecker {
when: &self.when, when: &self.when,
body, body,
@ -68,6 +88,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
storage_liveness, storage_liveness,
place_cache: FxHashSet::default(), place_cache: FxHashSet::default(),
value_cache: FxHashSet::default(), value_cache: FxHashSet::default(),
can_unwind,
}; };
cfg_checker.visit_body(body); cfg_checker.visit_body(body);
cfg_checker.check_cleanup_control_flow(); cfg_checker.check_cleanup_control_flow();
@ -99,6 +120,9 @@ struct CfgChecker<'a, 'tcx> {
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>, storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
place_cache: FxHashSet<PlaceRef<'tcx>>, place_cache: FxHashSet<PlaceRef<'tcx>>,
value_cache: FxHashSet<u128>, value_cache: FxHashSet<u128>,
// If `false`, then the MIR must not contain `UnwindAction::Continue` or
// `TerminatorKind::Resume`.
can_unwind: bool,
} }
impl<'a, 'tcx> CfgChecker<'a, 'tcx> { impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
@ -237,13 +261,17 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
match unwind { match unwind {
UnwindAction::Cleanup(unwind) => { UnwindAction::Cleanup(unwind) => {
if is_cleanup { if is_cleanup {
self.fail(location, "unwind on cleanup block"); self.fail(location, "`UnwindAction::Cleanup` in cleanup block");
} }
self.check_edge(location, unwind, EdgeKind::Unwind); self.check_edge(location, unwind, EdgeKind::Unwind);
} }
UnwindAction::Continue => { UnwindAction::Continue => {
if is_cleanup { if is_cleanup {
self.fail(location, "unwind on cleanup block"); self.fail(location, "`UnwindAction::Continue` in cleanup block");
}
if !self.can_unwind {
self.fail(location, "`UnwindAction::Continue` in no-unwind function");
} }
} }
UnwindAction::Unreachable | UnwindAction::Terminate => (), UnwindAction::Unreachable | UnwindAction::Terminate => (),
@ -464,13 +492,19 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
); );
} }
} }
TerminatorKind::Resume | TerminatorKind::Terminate => { TerminatorKind::Resume => {
let bb = location.block; let bb = location.block;
if !self.body.basic_blocks[bb].is_cleanup { if !self.body.basic_blocks[bb].is_cleanup {
self.fail( self.fail(location, "Cannot `Resume` from non-cleanup basic block")
location, }
"Cannot `Resume` or `Terminate` from non-cleanup basic block", if !self.can_unwind {
) self.fail(location, "Cannot `Resume` in a function that cannot unwind")
}
}
TerminatorKind::Terminate => {
let bb = location.block;
if !self.body.basic_blocks[bb].is_cleanup {
self.fail(location, "Cannot `Terminate` from non-cleanup basic block")
} }
} }
TerminatorKind::Return => { TerminatorKind::Return => {