diff options
| author | Gary Guo <gary@garyguo.net> | 2022-01-14 23:54:26 +0000 |
|---|---|---|
| committer | Gary Guo <gary@garyguo.net> | 2022-02-13 03:10:09 +0000 |
| commit | f74e8c7afcf803d6c78a1f05ab89c0e00bce5f0a (patch) | |
| tree | 380d2e6d5df5a0d1fc181ce9f9041261b2748ba9 /compiler/rustc_codegen_ssa/src | |
| parent | 3cfa4def7c87d571bd46d92fed608edf8fad236e (diff) | |
| download | rust-f74e8c7afcf803d6c78a1f05ab89c0e00bce5f0a.tar.gz rust-f74e8c7afcf803d6c78a1f05ab89c0e00bce5f0a.zip | |
Guard against unwinding in cleanup code
Diffstat (limited to 'compiler/rustc_codegen_ssa/src')
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/block.rs | 67 | ||||
| -rw-r--r-- | compiler/rustc_codegen_ssa/src/mir/mod.rs | 4 |
2 files changed, 58 insertions, 13 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 4c7a09ca1e9..5cbbb34d2e6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -135,21 +135,38 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { // If there is a cleanup block and the function we're calling can unwind, then // do an invoke, otherwise do a call. let fn_ty = bx.fn_decl_backend_type(&fn_abi); - if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) { + + let unwind_block = if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) { + Some(self.llblock(fx, cleanup)) + } else if fx.mir[self.bb].is_cleanup + && fn_abi.can_unwind + && !base::wants_msvc_seh(fx.cx.tcx().sess) + { + // Exception must not propagate out of the execution of a cleanup (doing so + // can cause undefined behaviour). We insert a double unwind guard for + // functions that can potentially unwind to protect against this. + // + // This is not necessary for SEH which does not use successive unwinding + // like Itanium EH. EH frames in SEH are different from normal function + // frames and SEH will abort automatically if an exception tries to + // propagate out from cleanup. + Some(fx.double_unwind_guard()) + } else { + None + }; + + if let Some(unwind_block) = unwind_block { let ret_llbb = if let Some((_, target)) = destination { fx.llbb(target) } else { fx.unreachable_block() }; - let invokeret = bx.invoke( - fn_ty, - fn_ptr, - &llargs, - ret_llbb, - self.llblock(fx, cleanup), - self.funclet(fx), - ); + let invokeret = + bx.invoke(fn_ty, fn_ptr, &llargs, ret_llbb, unwind_block, self.funclet(fx)); bx.apply_attrs_callsite(&fn_abi, invokeret); + if fx.mir[self.bb].is_cleanup { + bx.apply_attrs_to_cleanup_callsite(invokeret); + } if let Some((ret_dest, target)) = destination { let mut ret_bx = fx.build_block(target); @@ -486,9 +503,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let span = terminator.source_info.span; self.set_debug_loc(&mut bx, terminator.source_info); - // Get the location information. - let location = self.get_caller_location(&mut bx, terminator.source_info).immediate(); - // Obtain the panic entry point. let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::PanicNoUnwind); let instance = ty::Instance::mono(bx.tcx(), def_id); @@ -496,7 +510,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let llfn = bx.get_fn_addr(instance); // Codegen the actual panic invoke/call. - helper.do_call(self, &mut bx, fn_abi, llfn, &[location], None, None); + helper.do_call(self, &mut bx, fn_abi, llfn, &[], None, None); } /// Returns `true` if this is indeed a panic intrinsic and codegen is done. @@ -1398,6 +1412,33 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) } + fn double_unwind_guard(&mut self) -> Bx::BasicBlock { + self.double_unwind_guard.unwrap_or_else(|| { + assert!(!base::wants_msvc_seh(self.cx.sess())); + + let mut bx = self.new_block("abort"); + let llpersonality = self.cx.eh_personality(); + let llretty = self.landing_pad_type(); + bx.cleanup_landing_pad(llretty, llpersonality); + + let def_id = common::langcall(bx.tcx(), None, "", LangItem::PanicNoUnwind); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); + let fn_ptr = bx.get_fn_addr(instance); + let fn_ty = bx.fn_decl_backend_type(&fn_abi); + + let llret = bx.call(fn_ty, fn_ptr, &[], None); + bx.apply_attrs_callsite(&fn_abi, llret); + bx.apply_attrs_to_cleanup_callsite(llret); + + bx.unreachable(); + let llbb = bx.llbb(); + + self.double_unwind_guard = Some(llbb); + llbb + }) + } + // FIXME(eddyb) replace with `build_sibling_block`/`append_sibling_block` // (which requires having a `Bx` already, and not all callers do). fn new_block(&self, name: &str) -> Bx { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 814e4d626e1..4f05d02526e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -62,6 +62,9 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// Cached unreachable block unreachable_block: Option<Bx::BasicBlock>, + /// Cached double unwind guarding block + double_unwind_guard: Option<Bx::BasicBlock>, + /// The location where each MIR arg/var/tmp/ret is stored. This is /// usually an `PlaceRef` representing an alloca, but not always: /// sometimes we can skip the alloca and just store the value @@ -169,6 +172,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( personality_slot: None, cached_llbbs, unreachable_block: None, + double_unwind_guard: None, cleanup_kinds, landing_pads: IndexVec::from_elem(None, mir.basic_blocks()), funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks().len()), |
