rustc: Switch extern functions to abort by default on panic

This was intended to land way back in 1.24, but it was backed out due to
breakage which has long since been fixed. An unstable `#[unwind]`
attribute can be used to tweak the behavior here, but this is currently
simply switching rustc's internal default to abort-by-default if an
`extern` function panics, making our codegen sound primarily (as
currently you can produce UB with safe code)

Closes #52652
This commit is contained in:
Alex Crichton 2018-11-15 06:17:58 -08:00
parent bd47d6825b
commit 1091eee65b
10 changed files with 61 additions and 46 deletions

View File

@ -15,7 +15,7 @@ use rustc::hir::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::hir::def_id::{DefId, LOCAL_CRATE};
use rustc::session::Session; use rustc::session::Session;
use rustc::session::config::Sanitizer; use rustc::session::config::Sanitizer;
use rustc::ty::TyCtxt; use rustc::ty::{self, TyCtxt, PolyFnSig};
use rustc::ty::layout::HasTyCtxt; use rustc::ty::layout::HasTyCtxt;
use rustc::ty::query::Providers; use rustc::ty::query::Providers;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
@ -23,6 +23,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_target::spec::PanicStrategy; use rustc_target::spec::PanicStrategy;
use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::traits::*;
use abi::Abi;
use attributes; use attributes;
use llvm::{self, Attribute}; use llvm::{self, Attribute};
use llvm::AttributePlace::Function; use llvm::AttributePlace::Function;
@ -60,7 +61,7 @@ pub fn emit_uwtable(val: &'ll Value, emit: bool) {
/// Tell LLVM whether the function can or cannot unwind. /// Tell LLVM whether the function can or cannot unwind.
#[inline] #[inline]
pub fn unwind(val: &'ll Value, can_unwind: bool) { fn unwind(val: &'ll Value, can_unwind: bool) {
Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind); Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind);
} }
@ -150,9 +151,10 @@ pub fn non_lazy_bind(sess: &Session, llfn: &'ll Value) {
/// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
/// attributes. /// attributes.
pub fn from_fn_attrs( pub fn from_fn_attrs(
cx: &CodegenCx<'ll, '_>, cx: &CodegenCx<'ll, 'tcx>,
llfn: &'ll Value, llfn: &'ll Value,
id: Option<DefId>, id: Option<DefId>,
sig: PolyFnSig<'tcx>,
) { ) {
let codegen_fn_attrs = id.map(|id| cx.tcx.codegen_fn_attrs(id)) let codegen_fn_attrs = id.map(|id| cx.tcx.codegen_fn_attrs(id))
.unwrap_or_else(|| CodegenFnAttrs::new()); .unwrap_or_else(|| CodegenFnAttrs::new());
@ -194,28 +196,37 @@ pub fn from_fn_attrs(
llvm::AttributePlace::ReturnValue, llfn); llvm::AttributePlace::ReturnValue, llfn);
} }
let can_unwind = if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) { unwind(llfn, if cx.tcx.sess.panic_strategy() != PanicStrategy::Unwind {
Some(true) // In panic=abort mode we assume nothing can unwind anywhere, so
// optimize based on this!
false
} else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) {
// If a specific #[unwind] attribute is present, use that
true
} else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) {
Some(false) // Special attribute for allocator functions, which can't unwind
false
// Perhaps questionable, but we assume that anything defined } else if let Some(id) = id {
// *in Rust code* may unwind. Foreign items like `extern "C" { let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
// fn foo(); }` are assumed not to unwind **unless** they have if cx.tcx.is_foreign_item(id) {
// a `#[unwind]` attribute. // Foreign items like `extern "C" { fn foo(); }` are assumed not to
} else if id.map(|id| !cx.tcx.is_foreign_item(id)).unwrap_or(false) { // unwind
Some(true) false
} else { } else if sig.abi != Abi::Rust && sig.abi != Abi::RustCall {
None // Any items defined in Rust that *don't* have the `extern` ABI are
}; // defined to not unwind. We insert shims to abort if an unwind
// happens to enforce this.
match can_unwind { false
Some(false) => attributes::unwind(llfn, false), } else {
Some(true) if cx.tcx.sess.panic_strategy() == PanicStrategy::Unwind => { // Anything else defined in Rust is assumed that it can possibly
attributes::unwind(llfn, true); // unwind
true
} }
Some(true) | None => {} } else {
} // assume this can possibly unwind, avoiding the application of a
// `nounwind` attribute below.
true
});
// Always annotate functions with the target-cpu they are compiled for. // Always annotate functions with the target-cpu they are compiled for.
// Without this, ThinLTO won't inline Rust functions into Clang generated // Without this, ThinLTO won't inline Rust functions into Clang generated

View File

@ -94,7 +94,7 @@ pub fn get_fn(
if instance.def.is_inline(tcx) { if instance.def.is_inline(tcx) {
attributes::inline(cx, llfn, attributes::InlineAttr::Hint); attributes::inline(cx, llfn, attributes::InlineAttr::Hint);
} }
attributes::from_fn_attrs(cx, llfn, Some(instance.def.def_id())); attributes::from_fn_attrs(cx, llfn, Some(instance.def.def_id()), sig);
let instance_def_id = instance.def_id(); let instance_def_id = instance.def_id();

View File

@ -409,7 +409,6 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
)); ));
let llfn = self.declare_fn("rust_eh_unwind_resume", sig); let llfn = self.declare_fn("rust_eh_unwind_resume", sig);
attributes::unwind(llfn, true);
attributes::apply_target_cpu_attr(self, llfn); attributes::apply_target_cpu_attr(self, llfn);
unwresume.set(Some(llfn)); unwresume.set(Some(llfn));
llfn llfn

View File

@ -26,8 +26,7 @@ use rustc::ty::{self, PolyFnSig};
use rustc::ty::layout::LayoutOf; use rustc::ty::layout::LayoutOf;
use rustc::session::config::Sanitizer; use rustc::session::config::Sanitizer;
use rustc_data_structures::small_c_str::SmallCStr; use rustc_data_structures::small_c_str::SmallCStr;
use rustc_target::spec::PanicStrategy; use abi::{FnType, FnTypeExt};
use abi::{Abi, FnType, FnTypeExt};
use attributes; use attributes;
use context::CodegenCx; use context::CodegenCx;
use type_::Type; use type_::Type;
@ -86,10 +85,6 @@ fn declare_raw_fn(
_ => {}, _ => {},
} }
if cx.tcx.sess.panic_strategy() != PanicStrategy::Unwind {
attributes::unwind(llfn, false);
}
attributes::non_lazy_bind(cx.sess(), llfn); attributes::non_lazy_bind(cx.sess(), llfn);
llfn llfn
@ -132,10 +127,6 @@ impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> {
llvm::Attribute::NoReturn.apply_llfn(Function, llfn); llvm::Attribute::NoReturn.apply_llfn(Function, llfn);
} }
if sig.abi != Abi::Rust && sig.abi != Abi::RustCall {
attributes::unwind(llfn, false);
}
fty.apply_attrs_llfn(llfn); fty.apply_attrs_llfn(llfn);
llfn llfn

View File

@ -1081,7 +1081,7 @@ fn gen_fn<'ll, 'tcx>(
Abi::Rust Abi::Rust
)); ));
let llfn = cx.define_internal_fn(name, rust_fn_sig); let llfn = cx.define_internal_fn(name, rust_fn_sig);
attributes::from_fn_attrs(cx, llfn, None); attributes::from_fn_attrs(cx, llfn, None, rust_fn_sig);
let bx = Builder::new_block(cx, llfn, "entry-block"); let bx = Builder::new_block(cx, llfn, "entry-block");
codegen(bx); codegen(bx);
llfn llfn

View File

@ -82,7 +82,12 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
if instance.def.is_inline(self.tcx) { if instance.def.is_inline(self.tcx) {
attributes::inline(self, lldecl, attributes::InlineAttr::Hint); attributes::inline(self, lldecl, attributes::InlineAttr::Hint);
} }
attributes::from_fn_attrs(self, lldecl, Some(instance.def.def_id())); attributes::from_fn_attrs(
self,
lldecl,
Some(instance.def.def_id()),
mono_sig,
);
self.instances.borrow_mut().insert(instance, lldecl); self.instances.borrow_mut().insert(instance, lldecl);
} }

View File

@ -628,12 +628,7 @@ fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
// unwind anyway. Don't stop them. // unwind anyway. Don't stop them.
let attrs = &tcx.get_attrs(fn_def_id); let attrs = &tcx.get_attrs(fn_def_id);
match attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs) { match attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs) {
None => { None => true,
// FIXME(rust-lang/rust#48251) -- Had to disable
// abort-on-panic for backwards compatibility reasons.
false
}
Some(UnwindAttr::Allowed) => false, Some(UnwindAttr::Allowed) => false,
Some(UnwindAttr::Aborts) => true, Some(UnwindAttr::Aborts) => true,
} }

View File

@ -0,0 +1,16 @@
// Copyright 2018 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.
// compile-flags: -O
#![crate_type = "lib"]
// CHECK: Function Attrs: norecurse nounwind
pub extern fn foo() {}

View File

@ -15,14 +15,11 @@
// ignore-cloudabi no env and process // ignore-cloudabi no env and process
// ignore-emscripten no processes // ignore-emscripten no processes
#![feature(unwind_attributes)]
use std::{env, panic}; use std::{env, panic};
use std::io::prelude::*; use std::io::prelude::*;
use std::io; use std::io;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
#[unwind(aborts)]
extern "C" fn panic_in_ffi() { extern "C" fn panic_in_ffi() {
panic!("Test"); panic!("Test");
} }

View File

@ -10,6 +10,7 @@
// run-pass // run-pass
// ignore-wasm32-bare no libc to test ffi with // ignore-wasm32-bare no libc to test ffi with
// ignore-emscripten blows the JS stack
#![feature(rustc_private)] #![feature(rustc_private)]