From 56b933763e9799f9deef86153d60988571188b49 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 28 Jun 2023 15:15:34 +0100 Subject: [PATCH] Add MIR validation for unwind out from nounwind functions --- .../src/transform/validate.rs | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 33e73ad6653..50ae30711a6 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -18,6 +18,7 @@ use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::always_storage_live_locals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; use rustc_target::abi::{Size, FIRST_VARIANT}; +use rustc_target::spec::abi::Abi; #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum EdgeKind { @@ -58,6 +59,25 @@ impl<'tcx> MirPass<'tcx> for Validator { .iterate_to_fixpoint() .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 { when: &self.when, body, @@ -68,6 +88,7 @@ impl<'tcx> MirPass<'tcx> for Validator { storage_liveness, place_cache: FxHashSet::default(), value_cache: FxHashSet::default(), + can_unwind, }; cfg_checker.visit_body(body); cfg_checker.check_cleanup_control_flow(); @@ -99,6 +120,9 @@ struct CfgChecker<'a, 'tcx> { storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>, place_cache: FxHashSet>, value_cache: FxHashSet, + // If `false`, then the MIR must not contain `UnwindAction::Continue` or + // `TerminatorKind::Resume`. + can_unwind: bool, } impl<'a, 'tcx> CfgChecker<'a, 'tcx> { @@ -237,13 +261,17 @@ impl<'a, 'tcx> CfgChecker<'a, 'tcx> { match unwind { UnwindAction::Cleanup(unwind) => { 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); } UnwindAction::Continue => { 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 => (), @@ -464,13 +492,19 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { ); } } - TerminatorKind::Resume | TerminatorKind::Terminate => { + TerminatorKind::Resume => { let bb = location.block; if !self.body.basic_blocks[bb].is_cleanup { - self.fail( - location, - "Cannot `Resume` or `Terminate` from non-cleanup basic block", - ) + self.fail(location, "Cannot `Resume` 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 => {