Auto merge of #46833 - diwic:7c-abort-ffi, r=arielb1
Prevent unwinding past FFI boundaries Second attempt to write a patch to solve this. r? @nikomatsakis ~~So, my biggest issue with this patch is the way the patch determines *what* functions should have an abort landing pad (in `construct_fn`). I would ideally have this code match [src/librustc_trans/callee.rs::get_fn](https://github.com/rust-lang/rust/blob/master/src/librustc_trans/callee.rs#L107-L115) but couldn't find an id that returns true for `is_foreign_item`. Also tried `tcx.has_attr("unwind")` with no luck.~~ FIXED Other issues: * llvm.trap is an SIGILL on amd64. Ideally we could use panic-abort's version of aborting which is nicer but we don't want to depend on that library... * ~~Mir inlining is a stub currently.~~ FIXED (no-op) Also, when reviewing please take into account that I'm new to the code and only partially know what I'm doing... and that I've mostly made made matches on `TerminatorKind::Abort` match either `TerminatorKind::Resume` or `TerminatorKind::Unreachable` based on what looked best.
This commit is contained in:
commit
51b47dc4a1
1
src/Cargo.lock
generated
1
src/Cargo.lock
generated
@ -1919,6 +1919,7 @@ dependencies = [
|
||||
"log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc 0.0.0",
|
||||
"rustc_apfloat 0.0.0",
|
||||
"rustc_back 0.0.0",
|
||||
"rustc_const_eval 0.0.0",
|
||||
"rustc_const_math 0.0.0",
|
||||
"rustc_data_structures 0.0.0",
|
||||
|
@ -150,6 +150,7 @@ fn hash_stable<W: StableHasherResult>(&self,
|
||||
targets.hash_stable(hcx, hasher);
|
||||
}
|
||||
mir::TerminatorKind::Resume |
|
||||
mir::TerminatorKind::Abort |
|
||||
mir::TerminatorKind::Return |
|
||||
mir::TerminatorKind::GeneratorDrop |
|
||||
mir::TerminatorKind::Unreachable => {}
|
||||
|
@ -637,6 +637,10 @@ pub enum TerminatorKind<'tcx> {
|
||||
/// continue. Emitted by build::scope::diverge_cleanup.
|
||||
Resume,
|
||||
|
||||
/// Indicates that the landing pad is finished and that the process
|
||||
/// should abort. Used to prevent unwinding for foreign items.
|
||||
Abort,
|
||||
|
||||
/// Indicates a normal return. The return place should have
|
||||
/// been filled in by now. This should occur at most once.
|
||||
Return,
|
||||
@ -759,7 +763,7 @@ pub fn successors(&self) -> Cow<[BasicBlock]> {
|
||||
match *self {
|
||||
Goto { target: ref b } => slice::from_ref(b).into_cow(),
|
||||
SwitchInt { targets: ref b, .. } => b[..].into_cow(),
|
||||
Resume | GeneratorDrop => (&[]).into_cow(),
|
||||
Resume | Abort | GeneratorDrop => (&[]).into_cow(),
|
||||
Return => (&[]).into_cow(),
|
||||
Unreachable => (&[]).into_cow(),
|
||||
Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(),
|
||||
@ -794,7 +798,7 @@ pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> {
|
||||
match *self {
|
||||
Goto { target: ref mut b } => vec![b],
|
||||
SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
|
||||
Resume | GeneratorDrop => Vec::new(),
|
||||
Resume | Abort | GeneratorDrop => Vec::new(),
|
||||
Return => Vec::new(),
|
||||
Unreachable => Vec::new(),
|
||||
Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c],
|
||||
@ -823,6 +827,7 @@ pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
|
||||
match *self {
|
||||
TerminatorKind::Goto { .. } |
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::Abort |
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
@ -918,6 +923,7 @@ pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
|
||||
Return => write!(fmt, "return"),
|
||||
GeneratorDrop => write!(fmt, "generator_drop"),
|
||||
Resume => write!(fmt, "resume"),
|
||||
Abort => write!(fmt, "abort"),
|
||||
Yield { ref value, .. } => write!(fmt, "_1 = suspend({:?})", value),
|
||||
Unreachable => write!(fmt, "unreachable"),
|
||||
Drop { ref location, .. } => write!(fmt, "drop({:?})", location),
|
||||
@ -970,7 +976,7 @@ pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
|
||||
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
|
||||
use self::TerminatorKind::*;
|
||||
match *self {
|
||||
Return | Resume | Unreachable | GeneratorDrop => vec![],
|
||||
Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
|
||||
Goto { .. } => vec!["".into()],
|
||||
SwitchInt { ref values, .. } => {
|
||||
values.iter()
|
||||
@ -2102,6 +2108,7 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F)
|
||||
},
|
||||
GeneratorDrop => GeneratorDrop,
|
||||
Resume => Resume,
|
||||
Abort => Abort,
|
||||
Return => Return,
|
||||
Unreachable => Unreachable,
|
||||
FalseEdges { real_target, ref imaginary_targets } =>
|
||||
@ -2143,6 +2150,7 @@ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
|
||||
},
|
||||
Goto { .. } |
|
||||
Resume |
|
||||
Abort |
|
||||
Return |
|
||||
GeneratorDrop |
|
||||
Unreachable |
|
||||
|
@ -432,6 +432,7 @@ fn super_terminator_kind(&mut self,
|
||||
}
|
||||
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::Abort |
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Unreachable => {
|
||||
|
@ -14,6 +14,7 @@ graphviz = { path = "../libgraphviz" }
|
||||
log = "0.3"
|
||||
log_settings = "0.1.1"
|
||||
rustc = { path = "../librustc" }
|
||||
rustc_back = { path = "../librustc_back" }
|
||||
rustc_const_eval = { path = "../librustc_const_eval" }
|
||||
rustc_const_math = { path = "../librustc_const_math" }
|
||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||
|
@ -552,6 +552,7 @@ fn visit_terminator_entry(
|
||||
});
|
||||
}
|
||||
TerminatorKind::Goto { target: _ }
|
||||
| TerminatorKind::Abort
|
||||
| TerminatorKind::Unreachable
|
||||
| TerminatorKind::FalseEdges { .. } => {
|
||||
// no data used, thus irrelevant to borrowck
|
||||
|
@ -780,6 +780,7 @@ fn check_terminator(
|
||||
match term.kind {
|
||||
TerminatorKind::Goto { .. }
|
||||
| TerminatorKind::Resume
|
||||
| TerminatorKind::Abort
|
||||
| TerminatorKind::Return
|
||||
| TerminatorKind::GeneratorDrop
|
||||
| TerminatorKind::Unreachable
|
||||
@ -1082,6 +1083,9 @@ fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx>
|
||||
TerminatorKind::Resume => if !is_cleanup {
|
||||
span_mirbug!(self, block_data, "resume on non-cleanup block!")
|
||||
},
|
||||
TerminatorKind::Abort => if !is_cleanup {
|
||||
span_mirbug!(self, block_data, "abort on non-cleanup block!")
|
||||
},
|
||||
TerminatorKind::Return => if is_cleanup {
|
||||
span_mirbug!(self, block_data, "return on cleanup block")
|
||||
},
|
||||
|
@ -20,6 +20,7 @@
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::util::nodemap::NodeMap;
|
||||
use rustc_back::PanicStrategy;
|
||||
use rustc_const_eval::pattern::{BindingMode, PatternKind};
|
||||
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
|
||||
use shim;
|
||||
@ -353,6 +354,27 @@ macro_rules! unpack {
|
||||
};
|
||||
}
|
||||
|
||||
fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||
fn_id: ast::NodeId,
|
||||
abi: Abi)
|
||||
-> bool {
|
||||
|
||||
// Not callable from C, so we can safely unwind through these
|
||||
if abi == Abi::Rust || abi == Abi::RustCall { return false; }
|
||||
|
||||
// We never unwind, so it's not relevant to stop an unwind
|
||||
if tcx.sess.panic_strategy() != PanicStrategy::Unwind { return false; }
|
||||
|
||||
// We cannot add landing pads, so don't add one
|
||||
if tcx.sess.no_landing_pads() { return false; }
|
||||
|
||||
// This is a special case: some functions have a C abi but are meant to
|
||||
// unwind anyway. Don't stop them.
|
||||
if tcx.has_attr(tcx.hir.local_def_id(fn_id), "unwind") { return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// the main entry point for building MIR for a function
|
||||
|
||||
@ -383,6 +405,10 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
|
||||
let source_info = builder.source_info(span);
|
||||
let call_site_s = (call_site_scope, source_info);
|
||||
unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, block, |builder| {
|
||||
if should_abort_on_panic(tcx, fn_id, abi) {
|
||||
builder.schedule_abort();
|
||||
}
|
||||
|
||||
let arg_scope_s = (arg_scope, source_info);
|
||||
unpack!(block = builder.in_scope(arg_scope_s, LintLevel::Inherited, block, |builder| {
|
||||
builder.args_and_body(block, &arguments, arg_scope, &body.value)
|
||||
|
@ -612,6 +612,16 @@ pub fn local_scope(&self) -> Option<region::Scope> {
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule an abort block - this is used for some ABIs that cannot unwind
|
||||
pub fn schedule_abort(&mut self) -> BasicBlock {
|
||||
self.scopes[0].needs_cleanup = true;
|
||||
let abortblk = self.cfg.start_new_cleanup_block();
|
||||
let source_info = self.scopes[0].source_info(self.fn_span);
|
||||
self.cfg.terminate(abortblk, source_info, TerminatorKind::Abort);
|
||||
self.cached_resume_block = Some(abortblk);
|
||||
abortblk
|
||||
}
|
||||
|
||||
// Scheduling drops
|
||||
// ================
|
||||
/// Indicates that `place` should be dropped on exit from
|
||||
|
@ -496,6 +496,7 @@ fn terminator_effect_on_borrows(&self,
|
||||
}
|
||||
}
|
||||
}
|
||||
mir::TerminatorKind::Abort |
|
||||
mir::TerminatorKind::SwitchInt {..} |
|
||||
mir::TerminatorKind::Drop {..} |
|
||||
mir::TerminatorKind::DropAndReplace {..} |
|
||||
|
@ -771,6 +771,7 @@ fn propagate_bits_into_graph_successors_of(
|
||||
match bb_data.terminator().kind {
|
||||
mir::TerminatorKind::Return |
|
||||
mir::TerminatorKind::Resume |
|
||||
mir::TerminatorKind::Abort |
|
||||
mir::TerminatorKind::GeneratorDrop |
|
||||
mir::TerminatorKind::Unreachable => {}
|
||||
mir::TerminatorKind::Goto { ref target } |
|
||||
|
@ -334,6 +334,7 @@ fn gather_terminator(&mut self, term: &Terminator<'tcx>) {
|
||||
match term.kind {
|
||||
TerminatorKind::Goto { target: _ } |
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::Abort |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::FalseEdges { .. } |
|
||||
TerminatorKind::Unreachable => { }
|
||||
|
@ -163,6 +163,7 @@ pub(super) fn eval_terminator(
|
||||
GeneratorDrop => unimplemented!(),
|
||||
DropAndReplace { .. } => unimplemented!(),
|
||||
Resume => unimplemented!(),
|
||||
Abort => unimplemented!(),
|
||||
FalseEdges { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"),
|
||||
Unreachable => return err!(Unreachable),
|
||||
}
|
||||
|
@ -49,6 +49,7 @@
|
||||
#[macro_use]
|
||||
extern crate syntax;
|
||||
extern crate syntax_pos;
|
||||
extern crate rustc_back;
|
||||
extern crate rustc_const_math;
|
||||
extern crate rustc_const_eval;
|
||||
extern crate core; // for NonZero
|
||||
|
@ -625,6 +625,7 @@ fn visit_terminator_kind(&mut self,
|
||||
mir::TerminatorKind::Goto { .. } |
|
||||
mir::TerminatorKind::SwitchInt { .. } |
|
||||
mir::TerminatorKind::Resume |
|
||||
mir::TerminatorKind::Abort |
|
||||
mir::TerminatorKind::Return |
|
||||
mir::TerminatorKind::Unreachable |
|
||||
mir::TerminatorKind::Assert { .. } => {}
|
||||
|
@ -73,6 +73,7 @@ fn visit_terminator(&mut self,
|
||||
TerminatorKind::DropAndReplace { .. } |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::Abort |
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::FalseEdges { .. } => {
|
||||
|
@ -806,6 +806,7 @@ fn visit_terminator_kind(&mut self, block: BasicBlock,
|
||||
*kind = TerminatorKind::Goto { target: tgt }
|
||||
}
|
||||
}
|
||||
TerminatorKind::Abort => { }
|
||||
TerminatorKind::Unreachable => { }
|
||||
TerminatorKind::FalseEdges { ref mut real_target, ref mut imaginary_targets } => {
|
||||
*real_target = self.update_target(*real_target);
|
||||
|
@ -324,6 +324,7 @@ fn qualify_const(&mut self) -> (Qualif, Rc<IdxSetBuf<Local>>) {
|
||||
TerminatorKind::SwitchInt {..} |
|
||||
TerminatorKind::DropAndReplace { .. } |
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::Abort |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Yield { .. } |
|
||||
TerminatorKind::Unreachable |
|
||||
|
@ -76,6 +76,7 @@ fn is_nop_landing_pad(&self, bb: BasicBlock, mir: &Mir, nop_landing_pads: &BitVe
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Yield { .. } |
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::Abort |
|
||||
TerminatorKind::Unreachable |
|
||||
TerminatorKind::Call { .. } |
|
||||
TerminatorKind::Assert { .. } |
|
||||
|
@ -113,6 +113,7 @@ fn visit_terminator_kind(&mut self,
|
||||
TerminatorKind::Goto { .. } => "TerminatorKind::Goto",
|
||||
TerminatorKind::SwitchInt { .. } => "TerminatorKind::SwitchInt",
|
||||
TerminatorKind::Resume => "TerminatorKind::Resume",
|
||||
TerminatorKind::Abort => "TerminatorKind::Abort",
|
||||
TerminatorKind::Return => "TerminatorKind::Return",
|
||||
TerminatorKind::Unreachable => "TerminatorKind::Unreachable",
|
||||
TerminatorKind::Drop { .. } => "TerminatorKind::Drop",
|
||||
|
@ -236,6 +236,7 @@ fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
|
||||
match data.terminator().kind {
|
||||
TerminatorKind::Goto { .. } |
|
||||
TerminatorKind::Resume |
|
||||
TerminatorKind::Abort |
|
||||
TerminatorKind::Return |
|
||||
TerminatorKind::GeneratorDrop |
|
||||
TerminatorKind::Unreachable |
|
||||
|
@ -180,6 +180,13 @@ fn trans_terminator(&mut self,
|
||||
}
|
||||
}
|
||||
|
||||
mir::TerminatorKind::Abort => {
|
||||
// Call core::intrinsics::abort()
|
||||
let fnname = bcx.ccx.get_intrinsic(&("llvm.trap"));
|
||||
bcx.call(fnname, &[], None);
|
||||
bcx.unreachable();
|
||||
}
|
||||
|
||||
mir::TerminatorKind::Goto { target } => {
|
||||
funclet_br(self, bcx, target);
|
||||
}
|
||||
|
43
src/test/run-pass/abort-on-c-abi.rs
Normal file
43
src/test/run-pass/abort-on-c-abi.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Since we mark some ABIs as "nounwind" to LLVM, we must make sure that
|
||||
// we never unwind through them.
|
||||
|
||||
// ignore-emscripten no processes
|
||||
|
||||
use std::{env, panic};
|
||||
use std::io::prelude::*;
|
||||
use std::io;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
extern "C" fn panic_in_ffi() {
|
||||
panic!("Test");
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let _ = panic::catch_unwind(|| { panic_in_ffi(); });
|
||||
// The process should have aborted by now.
|
||||
io::stdout().write(b"This should never be printed.\n");
|
||||
let _ = io::stdout().flush();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() > 1 && args[1] == "test" {
|
||||
return test();
|
||||
}
|
||||
|
||||
let mut p = Command::new(&args[0])
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::piped())
|
||||
.arg("test").spawn().unwrap();
|
||||
assert!(!p.wait().unwrap().success());
|
||||
}
|
Loading…
Reference in New Issue
Block a user