Skip to content

Commit f74e8c7

Browse files
committed
Guard against unwinding in cleanup code
1 parent 3cfa4de commit f74e8c7

File tree

3 files changed

+59
-15
lines changed

3 files changed

+59
-15
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,21 +135,38 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
135135
// If there is a cleanup block and the function we're calling can unwind, then
136136
// do an invoke, otherwise do a call.
137137
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
138-
if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) {
138+
139+
let unwind_block = if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) {
140+
Some(self.llblock(fx, cleanup))
141+
} else if fx.mir[self.bb].is_cleanup
142+
&& fn_abi.can_unwind
143+
&& !base::wants_msvc_seh(fx.cx.tcx().sess)
144+
{
145+
// Exception must not propagate out of the execution of a cleanup (doing so
146+
// can cause undefined behaviour). We insert a double unwind guard for
147+
// functions that can potentially unwind to protect against this.
148+
//
149+
// This is not necessary for SEH which does not use successive unwinding
150+
// like Itanium EH. EH frames in SEH are different from normal function
151+
// frames and SEH will abort automatically if an exception tries to
152+
// propagate out from cleanup.
153+
Some(fx.double_unwind_guard())
154+
} else {
155+
None
156+
};
157+
158+
if let Some(unwind_block) = unwind_block {
139159
let ret_llbb = if let Some((_, target)) = destination {
140160
fx.llbb(target)
141161
} else {
142162
fx.unreachable_block()
143163
};
144-
let invokeret = bx.invoke(
145-
fn_ty,
146-
fn_ptr,
147-
&llargs,
148-
ret_llbb,
149-
self.llblock(fx, cleanup),
150-
self.funclet(fx),
151-
);
164+
let invokeret =
165+
bx.invoke(fn_ty, fn_ptr, &llargs, ret_llbb, unwind_block, self.funclet(fx));
152166
bx.apply_attrs_callsite(&fn_abi, invokeret);
167+
if fx.mir[self.bb].is_cleanup {
168+
bx.apply_attrs_to_cleanup_callsite(invokeret);
169+
}
153170

154171
if let Some((ret_dest, target)) = destination {
155172
let mut ret_bx = fx.build_block(target);
@@ -486,17 +503,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
486503
let span = terminator.source_info.span;
487504
self.set_debug_loc(&mut bx, terminator.source_info);
488505

489-
// Get the location information.
490-
let location = self.get_caller_location(&mut bx, terminator.source_info).immediate();
491-
492506
// Obtain the panic entry point.
493507
let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::PanicNoUnwind);
494508
let instance = ty::Instance::mono(bx.tcx(), def_id);
495509
let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
496510
let llfn = bx.get_fn_addr(instance);
497511

498512
// Codegen the actual panic invoke/call.
499-
helper.do_call(self, &mut bx, fn_abi, llfn, &[location], None, None);
513+
helper.do_call(self, &mut bx, fn_abi, llfn, &[], None, None);
500514
}
501515

502516
/// 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> {
13981412
})
13991413
}
14001414

1415+
fn double_unwind_guard(&mut self) -> Bx::BasicBlock {
1416+
self.double_unwind_guard.unwrap_or_else(|| {
1417+
assert!(!base::wants_msvc_seh(self.cx.sess()));
1418+
1419+
let mut bx = self.new_block("abort");
1420+
let llpersonality = self.cx.eh_personality();
1421+
let llretty = self.landing_pad_type();
1422+
bx.cleanup_landing_pad(llretty, llpersonality);
1423+
1424+
let def_id = common::langcall(bx.tcx(), None, "", LangItem::PanicNoUnwind);
1425+
let instance = ty::Instance::mono(bx.tcx(), def_id);
1426+
let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
1427+
let fn_ptr = bx.get_fn_addr(instance);
1428+
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
1429+
1430+
let llret = bx.call(fn_ty, fn_ptr, &[], None);
1431+
bx.apply_attrs_callsite(&fn_abi, llret);
1432+
bx.apply_attrs_to_cleanup_callsite(llret);
1433+
1434+
bx.unreachable();
1435+
let llbb = bx.llbb();
1436+
1437+
self.double_unwind_guard = Some(llbb);
1438+
llbb
1439+
})
1440+
}
1441+
14011442
// FIXME(eddyb) replace with `build_sibling_block`/`append_sibling_block`
14021443
// (which requires having a `Bx` already, and not all callers do).
14031444
fn new_block(&self, name: &str) -> Bx {

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
6262
/// Cached unreachable block
6363
unreachable_block: Option<Bx::BasicBlock>,
6464

65+
/// Cached double unwind guarding block
66+
double_unwind_guard: Option<Bx::BasicBlock>,
67+
6568
/// The location where each MIR arg/var/tmp/ret is stored. This is
6669
/// usually an `PlaceRef` representing an alloca, but not always:
6770
/// 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>>(
169172
personality_slot: None,
170173
cached_llbbs,
171174
unreachable_block: None,
175+
double_unwind_guard: None,
172176
cleanup_kinds,
173177
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
174178
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks().len()),

library/core/src/panicking.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ fn panic_bounds_check(index: usize, len: usize) -> ! {
8787

8888
#[cfg(not(bootstrap))]
8989
#[cold]
90-
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
91-
#[track_caller]
90+
#[inline(never)]
9291
#[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function
9392
fn panic_no_unwind() -> ! {
9493
if cfg!(feature = "panic_immediate_abort") {

0 commit comments

Comments
 (0)