Skip to content

Commit

Permalink
Rollup merge of #93153 - tmiasko:reject-unsupported-naked-functions, …
Browse files Browse the repository at this point in the history
…r=Amanieu

Reject unsupported naked functions

Transition unsupported naked functions future incompatibility lint into an error:

* Naked functions must contain a single inline assembly block. Introduced as future incompatibility lint in 1.50 #79653. Change into an error fixes a soundness issue described in #32489.

* Naked functions must not use any forms of inline attribute. Introduced as future incompatibility lint in 1.56 #87652.

Closes #32490.
Closes #32489.

r? ```@Amanieu``` ```@npmccallum``` ```@joshtriplett```
  • Loading branch information
matthiaskrgr committed Jan 22, 2022
2 parents 5fd9c05 + beeba4b commit a8f64c0
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 283 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,7 @@ E0783: include_str!("./error_codes/E0783.md"),
E0784: include_str!("./error_codes/E0784.md"),
E0785: include_str!("./error_codes/E0785.md"),
E0786: include_str!("./error_codes/E0786.md"),
E0787: include_str!("./error_codes/E0787.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0787.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
An unsupported naked function definition.

Erroneous code example:

```compile_fail,E0787
#![feature(naked_functions)]
#[naked]
pub extern "C" fn f() -> u32 {
42
}
```

The naked functions must be defined using a single inline assembly
block.

The execution must never fall through past the end of the assembly
code so the block must use `noreturn` option. The asm block can also
use `att_syntax` and `raw` options, but others options are not allowed.

The asm block must not contain any operands other than `const` and
`sym`.

### Additional information

For more information, please see [RFC 2972].

[RFC 2972]: https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md
5 changes: 5 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,11 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
<https://github.com/rust-lang/rust/issues/59014> for more information",
);
store.register_removed("plugin_as_library", "plugins have been deprecated and retired");
store.register_removed(
"unsupported_naked_functions",
"converted into hard error, see RFC 2972 \
<https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md> for more information",
);
}

fn register_internals(store: &mut LintStore) {
Expand Down
47 changes: 0 additions & 47 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2759,52 +2759,6 @@ declare_lint! {
"undefined naked function ABI"
}

declare_lint! {
/// The `unsupported_naked_functions` lint detects naked function
/// definitions that are unsupported but were previously accepted.
///
/// ### Example
///
/// ```rust
/// #![feature(naked_functions)]
///
/// #[naked]
/// pub extern "C" fn f() -> u32 {
/// 42
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// The naked functions must be defined using a single inline assembly
/// block.
///
/// The execution must never fall through past the end of the assembly
/// code so the block must use `noreturn` option. The asm block can also
/// use `att_syntax` option, but other options are not allowed.
///
/// The asm block must not contain any operands other than `const` and
/// `sym`. Additionally, naked function should specify a non-Rust ABI.
///
/// Naked functions cannot be inlined. All forms of the `inline` attribute
/// are prohibited.
///
/// While other definitions of naked functions were previously accepted,
/// they are unsupported and might not work reliably. This is a
/// [future-incompatible] lint that will transition into hard error in
/// the future.
///
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub UNSUPPORTED_NAKED_FUNCTIONS,
Warn,
"unsupported naked function definitions",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #32408 <https://github.com/rust-lang/rust/issues/32408>",
};
}

declare_lint! {
/// The `ineffective_unstable_trait_impl` lint detects `#[unstable]` attributes which are not used.
///
Expand Down Expand Up @@ -3070,7 +3024,6 @@ declare_lint_pass! {
UNINHABITED_STATIC,
FUNCTION_ITEM_REFERENCES,
USELESS_DEPRECATED,
UNSUPPORTED_NAKED_FUNCTIONS,
MISSING_ABI,
INVALID_DOC_ATTRIBUTES,
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
Expand Down
92 changes: 47 additions & 45 deletions compiler/rustc_passes/src/naked_functions.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//! Checks validity of naked functions.

use rustc_ast::{Attribute, InlineAsmOptions};
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{FnKind, Visitor};
use rustc_hir::{ExprKind, HirId, InlineAsmOperand, StmtKind};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::UNDEFINED_NAKED_FUNCTION_ABI;
use rustc_session::lint::builtin::UNSUPPORTED_NAKED_FUNCTIONS;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
Expand Down Expand Up @@ -64,18 +64,16 @@ impl<'tcx> Visitor<'tcx> for CheckNakedFunctions<'tcx> {
check_abi(self.tcx, hir_id, fn_header.abi, ident_span);
check_no_patterns(self.tcx, body.params);
check_no_parameters_use(self.tcx, body);
check_asm(self.tcx, hir_id, body, span);
check_inline(self.tcx, hir_id, attrs);
check_asm(self.tcx, body, span);
check_inline(self.tcx, attrs);
}
}
}

/// Check that the function isn't inlined.
fn check_inline(tcx: TyCtxt<'_>, hir_id: HirId, attrs: &[Attribute]) {
fn check_inline(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
for attr in attrs.iter().filter(|attr| attr.has_name(sym::inline)) {
tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, attr.span, |lint| {
lint.build("naked functions cannot be inlined").emit();
});
tcx.sess.struct_span_err(attr.span, "naked functions cannot be inlined").emit();
}
}

Expand Down Expand Up @@ -146,31 +144,31 @@ impl<'tcx> Visitor<'tcx> for CheckParameters<'tcx> {
}

/// Checks that function body contains a single inline assembly block.
fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, hir_id: HirId, body: &'tcx hir::Body<'tcx>, fn_span: Span) {
fn check_asm<'tcx>(tcx: TyCtxt<'tcx>, body: &'tcx hir::Body<'tcx>, fn_span: Span) {
let mut this = CheckInlineAssembly { tcx, items: Vec::new() };
this.visit_body(body);
if let [(ItemKind::Asm, _)] = this.items[..] {
// Ok.
} else {
tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, fn_span, |lint| {
let mut diag = lint.build("naked functions must contain a single asm block");
let mut has_asm = false;
for &(kind, span) in &this.items {
match kind {
ItemKind::Asm if has_asm => {
diag.span_label(
span,
"multiple asm blocks are unsupported in naked functions",
);
}
ItemKind::Asm => has_asm = true,
ItemKind::NonAsm => {
diag.span_label(span, "non-asm is unsupported in naked functions");
}
let mut diag = struct_span_err!(
tcx.sess,
fn_span,
E0787,
"naked functions must contain a single asm block"
);
let mut has_asm = false;
for &(kind, span) in &this.items {
match kind {
ItemKind::Asm if has_asm => {
diag.span_label(span, "multiple asm blocks are unsupported in naked functions");
}
ItemKind::Asm => has_asm = true,
ItemKind::NonAsm => {
diag.span_label(span, "non-asm is unsupported in naked functions");
}
}
diag.emit();
});
}
diag.emit();
}
}

Expand Down Expand Up @@ -221,7 +219,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {

ExprKind::InlineAsm(ref asm) => {
self.items.push((ItemKind::Asm, span));
self.check_inline_asm(expr.hir_id, asm, span);
self.check_inline_asm(asm, span);
}

ExprKind::DropTemps(..) | ExprKind::Block(..) | ExprKind::Err => {
Expand All @@ -230,7 +228,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
}
}

fn check_inline_asm(&self, hir_id: HirId, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) {
fn check_inline_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>, span: Span) {
let unsupported_operands: Vec<Span> = asm
.operands
.iter()
Expand All @@ -243,18 +241,17 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
})
.collect();
if !unsupported_operands.is_empty() {
self.tcx.struct_span_lint_hir(
UNSUPPORTED_NAKED_FUNCTIONS,
hir_id,
struct_span_err!(
self.tcx.sess,
unsupported_operands,
|lint| {
lint.build("only `const` and `sym` operands are supported in naked functions")
.emit();
},
);
E0787,
"only `const` and `sym` operands are supported in naked functions",
)
.emit();
}

let unsupported_options: Vec<&'static str> = [
(InlineAsmOptions::MAY_UNWIND, "`may_unwind`"),
(InlineAsmOptions::NOMEM, "`nomem`"),
(InlineAsmOptions::NOSTACK, "`nostack`"),
(InlineAsmOptions::PRESERVES_FLAGS, "`preserves_flags`"),
Expand All @@ -266,19 +263,24 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
.collect();

if !unsupported_options.is_empty() {
self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| {
lint.build(&format!(
"asm options unsupported in naked functions: {}",
unsupported_options.join(", ")
))
.emit();
});
struct_span_err!(
self.tcx.sess,
span,
E0787,
"asm options unsupported in naked functions: {}",
unsupported_options.join(", ")
)
.emit();
}

if !asm.options.contains(InlineAsmOptions::NORETURN) {
self.tcx.struct_span_lint_hir(UNSUPPORTED_NAKED_FUNCTIONS, hir_id, span, |lint| {
lint.build("asm in naked functions must use `noreturn` option").emit();
});
struct_span_err!(
self.tcx.sess,
span,
E0787,
"asm in naked functions must use `noreturn` option"
)
.emit();
}
}
}
Expand Down
40 changes: 15 additions & 25 deletions src/test/codegen/naked-functions.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,32 @@
// compile-flags: -C no-prepopulate-passes
// needs-asm-support
// only-x86_64

#![crate_type = "lib"]
#![feature(naked_functions)]
use std::arch::asm;

// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}void @naked_empty()
#[no_mangle]
#[naked]
pub fn naked_empty() {
pub unsafe extern "C" fn naked_empty() {
// CHECK-NEXT: {{.+}}:
// CHECK-NEXT: ret void
// CHECK-NEXT: call void asm
// CHECK-NEXT: unreachable
asm!("ret",
options(noreturn));
}

// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_args_and_return(i64 %a, i64 %b)
#[no_mangle]
#[naked]
// CHECK-NEXT: define{{.*}}void @naked_with_args(i{{[0-9]+( %a)?}})
pub fn naked_with_args(a: isize) {
pub unsafe extern "C" fn naked_with_args_and_return(a: isize, b: isize) -> isize {
// CHECK-NEXT: {{.+}}:
// CHECK: ret void
}

// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_return()
#[no_mangle]
#[naked]
pub fn naked_with_return() -> isize {
// CHECK-NEXT: {{.+}}:
// CHECK-NEXT: ret i{{[0-9]+}} 0
0
}

// CHECK: Function Attrs: naked
// CHECK-NEXT: define{{.*}}i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+( %a)?}})
#[no_mangle]
#[naked]
pub fn naked_with_args_and_return(a: isize) -> isize {
// CHECK-NEXT: {{.+}}:
// CHECK: ret i{{[0-9]+}} 0
0
// CHECK-NEXT: call void asm
// CHECK-NEXT: unreachable
asm!("lea rax, [rdi + rsi]",
"ret",
options(noreturn));
}
1 change: 0 additions & 1 deletion src/test/codegen/naked-noinline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

use std::arch::asm;

#[inline(always)]
#[naked]
#[no_mangle]
pub unsafe extern "C" fn f() {
Expand Down

0 comments on commit a8f64c0

Please sign in to comment.