Skip to content

Make #[naked] an unsafe attribute #139753

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/rustc_builtin_macros/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,9 @@ builtin_macros_multiple_defaults = multiple declared defaults
.suggestion = make `{$ident}` default

builtin_macros_naked_functions_testing_attribute =
cannot use `#[naked]` with testing attributes
cannot use `#[unsafe(naked)]` with testing attributes
.label = function marked with testing attribute here
.naked_attribute = `#[naked]` is incompatible with testing attributes
.naked_attribute = `#[unsafe(naked)]` is incompatible with testing attributes

builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[default]`
.label = this enum needs a unit variant marked with `#[default]`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,9 @@ global_asm! {
}

#[cfg(all(not(jit), target_arch = "x86_64"))]
#[naked]
#[unsafe(naked)]
extern "C" fn naked_test() {
unsafe {
naked_asm!("ret");
}
naked_asm!("ret")
}

#[repr(C)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/error_codes/E0736.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Erroneous code example:

```compile_fail,E0736
#[inline]
#[naked]
#[unsafe(naked)]
fn foo() {}
```

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/error_codes/E0787.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Erroneous code example:
```compile_fail,E0787
#![feature(naked_functions)]

#[naked]
#[unsafe(naked)]
pub extern "C" fn f() -> u32 {
42
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[

// Linking:
gated!(
naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No,
unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No,
naked_functions, experimental!(naked)
),

Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_mir_build/src/check_unsafety.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,13 +564,17 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
}
}
ExprKind::InlineAsm(box InlineAsmExpr {
asm_macro: AsmMacro::Asm | AsmMacro::NakedAsm,
asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm),
ref operands,
template: _,
options: _,
line_spans: _,
}) => {
self.requires_unsafe(expr.span, UseOfInlineAssembly);
// The `naked` attribute and the `naked_asm!` block form one atomic unit of
// unsafety, and `naked_asm!` does not itself need to be wrapped in an unsafe block.
if let AsmMacro::Asm = asm_macro {
self.requires_unsafe(expr.span, UseOfInlineAssembly);
}

// For inline asm, do not use `walk_expr`, since we want to handle the label block
// specially.
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_parse/src/validate_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,6 @@ pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr:
}
}
} else if let Safety::Unsafe(unsafe_span) = attr_item.unsafety {
// Allow (but don't require) `#[unsafe(naked)]` so that compiler-builtins can upgrade to it.
// FIXME(#139797): remove this special case when compiler-builtins has upgraded.
if attr.has_name(sym::naked) {
return;
}

psess.dcx().emit_err(errors::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_item.path.clone(),
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -508,17 +508,17 @@ passes_must_use_no_effect =
`#[must_use]` has no effect when applied to {$article} {$target}

passes_naked_asm_outside_naked_fn =
the `naked_asm!` macro can only be used in functions marked with `#[naked]`
the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`

passes_naked_functions_asm_block =
naked functions must contain a single `naked_asm!` invocation
.label_multiple_asm = multiple `naked_asm!` invocations are not allowed in naked functions
.label_non_asm = not allowed in naked functions

passes_naked_functions_incompatible_attribute =
attribute incompatible with `#[naked]`
.label = the `{$attr}` attribute is incompatible with `#[naked]`
.naked_attribute = function marked with `#[naked]` here
attribute incompatible with `#[unsafe(naked)]`
.label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`
.naked_attribute = function marked with `#[unsafe(naked)]` here

passes_naked_functions_must_naked_asm =
the `asm!` macro is not allowed in naked functions
Expand Down
37 changes: 17 additions & 20 deletions src/doc/unstable-book/src/compiler-flags/sanitizer.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,34 +247,31 @@ See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
```rust,ignore (making doc tests pass cross-platform is hard)
#![feature(naked_functions)]

use std::arch::asm;
use std::arch::naked_asm;
use std::mem;

fn add_one(x: i32) -> i32 {
x + 1
}

#[naked]
#[unsafe(naked)]
pub extern "C" fn add_two(x: i32) {
// x + 2 preceded by a landing pad/nop block
unsafe {
asm!(
"
nop
nop
nop
nop
nop
nop
nop
nop
nop
lea eax, [rdi+2]
ret
",
options(noreturn)
);
}
naked_asm!(
"
nop
nop
nop
nop
nop
nop
nop
nop
nop
lea eax, [rdi+2]
ret
"
);
}

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use std::arch::naked_asm;
// LLVM implements this via making sure of that, even for functions with the naked attribute.
// So, we must emit an appropriate instruction instead!
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _hlt() -> ! {
#[unsafe(naked)]
pub extern "C" fn _hlt() -> ! {
// CHECK-NOT: hint #34
// CHECK: hlt #0x1
naked_asm!("hlt #1")
Expand Down
4 changes: 2 additions & 2 deletions tests/assembly/naked-functions/aix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use minicore::*;
// CHECK-LABEL: blr:
// CHECK: blr
#[no_mangle]
#[naked]
unsafe extern "C" fn blr() {
#[unsafe(naked)]
extern "C" fn blr() {
naked_asm!("blr")
}
60 changes: 30 additions & 30 deletions tests/assembly/naked-functions/wasm32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ use minicore::*;
// CHECK-NOT: .size
// CHECK: end_function
#[no_mangle]
#[naked]
unsafe extern "C" fn nop() {
#[unsafe(naked)]
extern "C" fn nop() {
naked_asm!("nop")
}

Expand All @@ -34,11 +34,11 @@ unsafe extern "C" fn nop() {
// CHECK-NOT: .size
// CHECK: end_function
#[no_mangle]
#[naked]
#[unsafe(naked)]
#[linkage = "weak"]
// wasm functions cannot be aligned, so this has no effect
#[repr(align(32))]
unsafe extern "C" fn weak_aligned_nop() {
extern "C" fn weak_aligned_nop() {
naked_asm!("nop")
}

Expand All @@ -51,48 +51,48 @@ unsafe extern "C" fn weak_aligned_nop() {
//
// CHECK-NEXT: end_function
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_i8_i8(num: i8) -> i8 {
#[unsafe(naked)]
extern "C" fn fn_i8_i8(num: i8) -> i8 {
naked_asm!("local.get 0", "local.get 0", "i32.mul")
}

// CHECK-LABEL: fn_i8_i8_i8:
// CHECK: .functype fn_i8_i8_i8 (i32, i32) -> (i32)
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_i8_i8_i8(a: i8, b: i8) -> i8 {
#[unsafe(naked)]
extern "C" fn fn_i8_i8_i8(a: i8, b: i8) -> i8 {
naked_asm!("local.get 1", "local.get 0", "i32.mul")
}

// CHECK-LABEL: fn_unit_i8:
// CHECK: .functype fn_unit_i8 () -> (i32)
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_unit_i8() -> i8 {
#[unsafe(naked)]
extern "C" fn fn_unit_i8() -> i8 {
naked_asm!("i32.const 42")
}

// CHECK-LABEL: fn_i8_unit:
// CHECK: .functype fn_i8_unit (i32) -> ()
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_i8_unit(_: i8) {
#[unsafe(naked)]
extern "C" fn fn_i8_unit(_: i8) {
naked_asm!("nop")
}

// CHECK-LABEL: fn_i32_i32:
// CHECK: .functype fn_i32_i32 (i32) -> (i32)
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_i32_i32(num: i32) -> i32 {
#[unsafe(naked)]
extern "C" fn fn_i32_i32(num: i32) -> i32 {
naked_asm!("local.get 0", "local.get 0", "i32.mul")
}

// CHECK-LABEL: fn_i64_i64:
// CHECK: .functype fn_i64_i64 (i64) -> (i64)
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_i64_i64(num: i64) -> i64 {
#[unsafe(naked)]
extern "C" fn fn_i64_i64(num: i64) -> i64 {
naked_asm!("local.get 0", "local.get 0", "i64.mul")
}

Expand All @@ -101,8 +101,8 @@ unsafe extern "C" fn fn_i64_i64(num: i64) -> i64 {
// wasm64-unknown: .functype fn_i128_i128 (i64, i64, i64) -> ()
#[allow(improper_ctypes_definitions)]
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_i128_i128(num: i128) -> i128 {
#[unsafe(naked)]
extern "C" fn fn_i128_i128(num: i128) -> i128 {
naked_asm!(
"local.get 0",
"local.get 2",
Expand All @@ -117,8 +117,8 @@ unsafe extern "C" fn fn_i128_i128(num: i128) -> i128 {
// wasm32-wasip1: .functype fn_f128_f128 (i32, i64, i64) -> ()
// wasm64-unknown: .functype fn_f128_f128 (i64, i64, i64) -> ()
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_f128_f128(num: f128) -> f128 {
#[unsafe(naked)]
extern "C" fn fn_f128_f128(num: f128) -> f128 {
naked_asm!(
"local.get 0",
"local.get 2",
Expand All @@ -139,8 +139,8 @@ struct Compound {
// wasm32-wasip1: .functype fn_compound_compound (i32, i32) -> ()
// wasm64-unknown: .functype fn_compound_compound (i64, i64) -> ()
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_compound_compound(_: Compound) -> Compound {
#[unsafe(naked)]
extern "C" fn fn_compound_compound(_: Compound) -> Compound {
// this is the wasm32-wasip1 assembly
naked_asm!(
"local.get 0",
Expand All @@ -160,8 +160,8 @@ struct WrapperI32(i32);
// CHECK-LABEL: fn_wrapperi32_wrapperi32:
// CHECK: .functype fn_wrapperi32_wrapperi32 (i32) -> (i32)
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_wrapperi32_wrapperi32(_: WrapperI32) -> WrapperI32 {
#[unsafe(naked)]
extern "C" fn fn_wrapperi32_wrapperi32(_: WrapperI32) -> WrapperI32 {
naked_asm!("local.get 0")
}

Expand All @@ -171,8 +171,8 @@ struct WrapperI64(i64);
// CHECK-LABEL: fn_wrapperi64_wrapperi64:
// CHECK: .functype fn_wrapperi64_wrapperi64 (i64) -> (i64)
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_wrapperi64_wrapperi64(_: WrapperI64) -> WrapperI64 {
#[unsafe(naked)]
extern "C" fn fn_wrapperi64_wrapperi64(_: WrapperI64) -> WrapperI64 {
naked_asm!("local.get 0")
}

Expand All @@ -182,8 +182,8 @@ struct WrapperF32(f32);
// CHECK-LABEL: fn_wrapperf32_wrapperf32:
// CHECK: .functype fn_wrapperf32_wrapperf32 (f32) -> (f32)
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_wrapperf32_wrapperf32(_: WrapperF32) -> WrapperF32 {
#[unsafe(naked)]
extern "C" fn fn_wrapperf32_wrapperf32(_: WrapperF32) -> WrapperF32 {
naked_asm!("local.get 0")
}

Expand All @@ -193,7 +193,7 @@ struct WrapperF64(f64);
// CHECK-LABEL: fn_wrapperf64_wrapperf64:
// CHECK: .functype fn_wrapperf64_wrapperf64 (f64) -> (f64)
#[no_mangle]
#[naked]
unsafe extern "C" fn fn_wrapperf64_wrapperf64(_: WrapperF64) -> WrapperF64 {
#[unsafe(naked)]
extern "C" fn fn_wrapperf64_wrapperf64(_: WrapperF64) -> WrapperF64 {
naked_asm!("local.get 0")
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use std::arch::naked_asm;
// works by using an instruction for each possible landing site,
// and LLVM implements this via making sure of that.
#[no_mangle]
#[naked]
pub unsafe extern "sysv64" fn will_halt() -> ! {
#[unsafe(naked)]
pub extern "sysv64" fn will_halt() -> ! {
// CHECK-NOT: endbr{{32|64}}
// CHECK: hlt
naked_asm!("hlt")
Expand Down
6 changes: 2 additions & 4 deletions tests/codegen/cffi/c-variadic-naked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
#![feature(naked_functions)]
#![no_std]

#[naked]
#[unsafe(naked)]
pub unsafe extern "C" fn c_variadic(_: usize, _: ...) {
// CHECK-NOT: va_start
// CHECK-NOT: alloca
core::arch::naked_asm! {
"ret",
}
core::arch::naked_asm!("ret")
}
4 changes: 2 additions & 2 deletions tests/codegen/naked-asan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ pub fn caller() {
}

// CHECK: declare x86_intrcc void @page_fault_handler(ptr {{.*}}, i64{{.*}}){{.*}}#[[ATTRS:[0-9]+]]
#[naked]
#[unsafe(naked)]
#[no_mangle]
pub extern "x86-interrupt" fn page_fault_handler(_: u64, _: u64) {
unsafe { core::arch::naked_asm!("ud2") };
core::arch::naked_asm!("ud2")
}

// CHECK: #[[ATTRS]] =
Expand Down
6 changes: 3 additions & 3 deletions tests/codegen/naked-fn/aligned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use std::arch::naked_asm;
// CHECK-LABEL: naked_empty:
#[repr(align(16))]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn naked_empty() {
#[unsafe(naked)]
pub extern "C" fn naked_empty() {
// CHECK: ret
naked_asm!("ret");
naked_asm!("ret")
}
Loading
Loading