Guard against unwinding in cleanup code
This commit is contained in:
parent
3cfa4def7c
commit
f74e8c7afc
3 changed files with 59 additions and 15 deletions
|
@ -135,21 +135,38 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
|
||||||
// If there is a cleanup block and the function we're calling can unwind, then
|
// If there is a cleanup block and the function we're calling can unwind, then
|
||||||
// do an invoke, otherwise do a call.
|
// do an invoke, otherwise do a call.
|
||||||
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
|
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 {
|
let ret_llbb = if let Some((_, target)) = destination {
|
||||||
fx.llbb(target)
|
fx.llbb(target)
|
||||||
} else {
|
} else {
|
||||||
fx.unreachable_block()
|
fx.unreachable_block()
|
||||||
};
|
};
|
||||||
let invokeret = bx.invoke(
|
let invokeret =
|
||||||
fn_ty,
|
bx.invoke(fn_ty, fn_ptr, &llargs, ret_llbb, unwind_block, self.funclet(fx));
|
||||||
fn_ptr,
|
|
||||||
&llargs,
|
|
||||||
ret_llbb,
|
|
||||||
self.llblock(fx, cleanup),
|
|
||||||
self.funclet(fx),
|
|
||||||
);
|
|
||||||
bx.apply_attrs_callsite(&fn_abi, invokeret);
|
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 {
|
if let Some((ret_dest, target)) = destination {
|
||||||
let mut ret_bx = fx.build_block(target);
|
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;
|
let span = terminator.source_info.span;
|
||||||
self.set_debug_loc(&mut bx, terminator.source_info);
|
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.
|
// Obtain the panic entry point.
|
||||||
let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::PanicNoUnwind);
|
let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::PanicNoUnwind);
|
||||||
let instance = ty::Instance::mono(bx.tcx(), def_id);
|
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);
|
let llfn = bx.get_fn_addr(instance);
|
||||||
|
|
||||||
// Codegen the actual panic invoke/call.
|
// 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.
|
/// 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`
|
// FIXME(eddyb) replace with `build_sibling_block`/`append_sibling_block`
|
||||||
// (which requires having a `Bx` already, and not all callers do).
|
// (which requires having a `Bx` already, and not all callers do).
|
||||||
fn new_block(&self, name: &str) -> Bx {
|
fn new_block(&self, name: &str) -> Bx {
|
||||||
|
|
|
@ -62,6 +62,9 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
|
||||||
/// Cached unreachable block
|
/// Cached unreachable block
|
||||||
unreachable_block: Option<Bx::BasicBlock>,
|
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
|
/// The location where each MIR arg/var/tmp/ret is stored. This is
|
||||||
/// usually an `PlaceRef` representing an alloca, but not always:
|
/// usually an `PlaceRef` representing an alloca, but not always:
|
||||||
/// sometimes we can skip the alloca and just store the value
|
/// 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,
|
personality_slot: None,
|
||||||
cached_llbbs,
|
cached_llbbs,
|
||||||
unreachable_block: None,
|
unreachable_block: None,
|
||||||
|
double_unwind_guard: None,
|
||||||
cleanup_kinds,
|
cleanup_kinds,
|
||||||
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
|
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
|
||||||
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks().len()),
|
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks().len()),
|
||||||
|
|
|
@ -87,8 +87,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! {
|
||||||
|
|
||||||
#[cfg(not(bootstrap))]
|
#[cfg(not(bootstrap))]
|
||||||
#[cold]
|
#[cold]
|
||||||
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
|
#[inline(never)]
|
||||||
#[track_caller]
|
|
||||||
#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function
|
#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function
|
||||||
fn panic_no_unwind() -> ! {
|
fn panic_no_unwind() -> ! {
|
||||||
if cfg!(feature = "panic_immediate_abort") {
|
if cfg!(feature = "panic_immediate_abort") {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue