Skip to content

Stabilize naked_functions #134213

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

folkertdev
Copy link
Contributor

@folkertdev folkertdev commented Dec 12, 2024

tracking issue: #90957
request for stabilization on tracking issue: #90957 (comment)
reference PR: rust-lang/reference#1689

Request for Stabilization

Two years later, we're ready to try this again. Even though this issue is already marked as having passed FCP, given the amount of time that has passed and the changes in implementation strategy, we should follow the process again.

Summary

The naked_functions feature has two main parts: the #[naked] function attribute, and the naked_asm! macro.

An example of a naked function:

const THREE: usize = 3;

#[naked]
pub extern "sysv64" fn add_n(number: usize) -> usize {
    // SAFETY: the validity of the used registers 
    // is guaranteed according to the "sysv64" ABI
    unsafe {
        core::arch::naked_asm!(
            "add rdi, {}",
            "mov rax, rdi",
            "ret",
            const THREE,
        )
    }
}

When the #[naked] attribute is applied to a function, the compiler won't emit a function prologue or epilogue when generating code for this function. This attribute is analogous to __attribute__((naked)) in C. The use of this feature allows the programmer to have precise control over the assembly that is generated for a given function.

The body of a naked function must consist of a single naked_asm! invocation, a heavily restricted variant of the asm! macro: the only legal operands are const and sym, and the only legal options are raw and att_syntax. In lieu of specifying operands, the naked_asm! within a naked function relies on the function's calling convention to determine the validity of registers.

Documentation

The Rust Reference: rust-lang/reference#1689
(Previous PR: rust-lang/reference#1153)

Tests

Interaction with other (unstable) features

fn_align

Combining #[naked] with #[repr(align(N))] works well, and is tested e.g. here

It's tested extensively because we do need to explicitly support the repr(align) attribute (and make sure we e.g. don't mistake powers of two for number of bytes).

History

This feature was originally proposed in RFC 1201, filed on 2015-07-10 and accepted on 2016-03-21. Support for this feature was added in #32410, landing on 2016-03-23. Development languished for several years as it was realized that the semantics given in RFC 1201 were insufficiently specific. To address this, a minimal subset of naked functions was specified by RFC 2972, filed on 2020-08-07 and accepted on 2021-11-16. Prior to the acceptance of RFC 2972, all of the stricter behavior specified by RFC 2972 was implemented as a series of warn-by-default lints that would trigger on existing uses of the naked attribute; these lints became hard errors in #93153 on 2022-01-22. As a result, today RFC 2972 has completely superseded RFC 1201 in describing the semantics of the naked attribute.

More recently, the naked_asm! macro was added to replace the earlier use of a heavily restricted asm! invocation. The naked_asm! name is clearer in error messages, and provides a place for documenting the specific requirements of inline assembly in naked functions.

The implementation strategy was changed to emitting a global assembly block. In effect, an extern function

extern "C" fn foo() {
    core::arch::naked_asm!("ret")
}

is emitted as something similar to

core::arch::global_asm!( 
    "foo:",
    "ret"
);

extern "C" {
    fn foo();
}

The codegen approach was chosen over the llvm naked function attribute because:

  • the rust compiler can guarantee the behavior (no sneaky additional instructions, no inlining, etc.)
  • behavior is the same on all backends (llvm, cranelift, gcc, etc)

Finally, there is now an allow list of compatible attributes on naked functions, so that e.g. #[inline] is rejected with an error. The #[target_feature] attribute on naked functions was later made separately unstable, because implementing it is complex and we did not want to block naked functions themselves on how target features work on them. See also #138568.

relevant PRs for these recent changes

Various historical notes

noreturn

RFC 2972 mentions that naked functions

must have a body which contains only a single asm!() statement which:
iii. must contain the noreturn option.

Instead of asm!, the current implementation mandates that the body contain a single naked_asm! statement. The naked_asm! macro is a heavily restricted version of the asm! macro, making it easier to talk about and document the rules of assembly in naked functions and give dedicated error messages.

For naked_asm!, the behavior of the asm!'s noreturn option is implicit. The noreturn option means that it is UB for control flow to fall through the end of the assembly block. With asm!, this option is usually used for blocks that diverge (and thus have no return and can be typed as !). With naked_asm!, the intent is different: usually naked funtions do return, but they must do so from within the assembly block. The noreturn option was used so that the compiler would not itself also insert a ret instruction at the very end.

padding / ud2

A naked_asm! block that violates the safety assumption that control flow must not fall through the end of the assembly block is UB. Because no return instruction is emitted, whatever bytes follow the naked function will be executed, resulting in truly undefined behavior. There has been discussion whether rustc should emit an invalid instruction (e.g. ud2 on x86) after the naked_asm! block to at least fail early in the case of an invalid naked_asm!. It was however decided that it is more useful to guarantee that #[naked] functions NEVER contain any instructions besides those in the naked_asm! block.

unresolved questions

None

r? @Amanieu

I've validated the tests on x86_64 and aarch64

@rustbot rustbot added A-run-make Area: port run-make Makefiles to rmake.rs PG-exploit-mitigations Project group: Exploit mitigations S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Dec 12, 2024
@rustbot
Copy link
Collaborator

rustbot commented Dec 12, 2024

Some changes occurred in src/doc/unstable-book/src/compiler-flags/sanitizer.md

cc @rust-lang/project-exploit-mitigations, @rcvalle

This PR modifies tests/run-make/. If this PR is trying to port a Makefile
run-make test to use rmake.rs, please update the
run-make port tracking issue
so we can track our progress. You can either modify the tracking issue
directly, or you can comment on the tracking issue and link this PR.

cc @jieyouxu

rust-analyzer is developed in its own repository. If possible, consider making this change to rust-lang/rust-analyzer instead.

cc @rust-lang/rust-analyzer

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

@traviscross traviscross added T-lang Relevant to the language team, which will review and decide on the PR/issue. S-waiting-on-documentation Status: Waiting on approved PRs to documentation before merging needs-fcp This change is insta-stable, so needs a completed FCP to proceed. I-lang-nominated Nominated for discussion during a lang team meeting. and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Dec 13, 2024
@bors
Copy link
Collaborator

bors commented Dec 16, 2024

☔ The latest upstream changes (presumably #134395) made this pull request unmergeable. Please resolve the merge conflicts.

@folkertdev folkertdev force-pushed the stabilize-naked-functions branch from 6183807 to ed0d0b9 Compare December 18, 2024 21:05
@folkertdev folkertdev force-pushed the stabilize-naked-functions branch from ed0d0b9 to ae2fa18 Compare January 8, 2025 18:39
@tgross35 tgross35 added the T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. label Jan 9, 2025
@Amanieu
Copy link
Member

Amanieu commented Jan 13, 2025

r? lang

@rustbot rustbot assigned tmandry and unassigned Amanieu Jan 13, 2025
@Amanieu
Copy link
Member

Amanieu commented Jan 13, 2025

This probably needs a new lang FCP since the old one is probably outdated (the implementation of naked function has changed signficantly).

@tmandry
Copy link
Member

tmandry commented Jan 18, 2025

Thanks for the thorough report @folkertdev!

@rfcbot fcp merge

@rfcbot
Copy link
Collaborator

rfcbot commented Jan 18, 2025

Team member @tmandry has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jan 18, 2025
@tmandry
Copy link
Member

tmandry commented Jan 18, 2025

Actually, @rust-lang/libs-api does this need your FCP? I think the path of core::arch::naked_asm! is the only part that might.

@traviscross traviscross removed the PG-exploit-mitigations Project group: Exploit mitigations label Jan 27, 2025
@bors
Copy link
Collaborator

bors commented Apr 9, 2025

☔ The latest upstream changes (presumably #139555) made this pull request unmergeable. Please resolve the merge conflicts.

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Apr 14, 2025
…gross35

Allow (but don't require) `#[unsafe(naked)]` so that `compiler-builtins` can upgrade to it

tracking issue: rust-lang#138997

Per rust-lang#134213 (comment), we want to make the `#[naked]` attribute an unsafe attribute. Making that change runs into a cyclic dependency with `compiler-builtins` which uses `#[naked]`, where `rustc` needs an updated `compiler-builtins` and vice versa.

So based on rust-lang#139753 and [#t-compiler/help > updating &rust-lang#96;compiler-builtins&rust-lang#96; and &rust-lang#96;rustc&rust-lang#96;](https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp/topic/updating.20.60compiler-builtins.60.20and.20.60rustc.60), this PR allows, but does not require `#[unsafe(naked)]`, and makes that change for some of the tests to check that both `#[naked]` and `#[unsafe(naked)]` are accepted.

Then we can upgrade and synchronize `compiler-builtins`, and then make `#[naked]` (without `unsafe`) invalid.

r? `@traviscross` (or someone from t-compiler if you're faster and this look allright)
Zalathar added a commit to Zalathar/rust that referenced this pull request Apr 15, 2025
…gross35

Allow (but don't require) `#[unsafe(naked)]` so that `compiler-builtins` can upgrade to it

tracking issue: rust-lang#138997

Per rust-lang#134213 (comment), we want to make the `#[naked]` attribute an unsafe attribute. Making that change runs into a cyclic dependency with `compiler-builtins` which uses `#[naked]`, where `rustc` needs an updated `compiler-builtins` and vice versa.

So based on rust-lang#139753 and [#t-compiler/help > updating &rust-lang#96;compiler-builtins&rust-lang#96; and &rust-lang#96;rustc&rust-lang#96;](https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp/topic/updating.20.60compiler-builtins.60.20and.20.60rustc.60), this PR allows, but does not require `#[unsafe(naked)]`, and makes that change for some of the tests to check that both `#[naked]` and `#[unsafe(naked)]` are accepted.

Then we can upgrade and synchronize `compiler-builtins`, and then make `#[naked]` (without `unsafe`) invalid.

r? ``@traviscross`` (or someone from t-compiler if you're faster and this look allright)
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Apr 15, 2025
Rollup merge of rust-lang#139797 - folkertdev:naked-allow-unsafe, r=tgross35

Allow (but don't require) `#[unsafe(naked)]` so that `compiler-builtins` can upgrade to it

tracking issue: rust-lang#138997

Per rust-lang#134213 (comment), we want to make the `#[naked]` attribute an unsafe attribute. Making that change runs into a cyclic dependency with `compiler-builtins` which uses `#[naked]`, where `rustc` needs an updated `compiler-builtins` and vice versa.

So based on rust-lang#139753 and [#t-compiler/help > updating &rust-lang#96;compiler-builtins&rust-lang#96; and &rust-lang#96;rustc&rust-lang#96;](https://rust-lang.zulipchat.com/#narrow/channel/182449-t-compiler.2Fhelp/topic/updating.20.60compiler-builtins.60.20and.20.60rustc.60), this PR allows, but does not require `#[unsafe(naked)]`, and makes that change for some of the tests to check that both `#[naked]` and `#[unsafe(naked)]` are accepted.

Then we can upgrade and synchronize `compiler-builtins`, and then make `#[naked]` (without `unsafe`) invalid.

r? `@traviscross` (or someone from t-compiler if you're faster and this look allright)
@U007D
Copy link

U007D commented Apr 18, 2025

Therefore it makes sense to move the unsafe outward to the attribute and say that what we mean in doing that is to extend the scope of discharge to the entire function, exactly as if we had item-level unsafe, as above. So we'll write:

#[unsafe(naked)]
extern "sysv64" fn f() {
    naked_asm! {
        ..
    }
}

Pleasingly, this also avoids some rightward drift.

I, too, would prefer to write the above.

I'm concerned though, that this may be inconsistent with other uses of unsafe attributes. Do all unsafe attributes extend the scope of their unsafe over the proceeding item? For example:

#[unsafe(no_mangle)]
pub fn example() { /* Are we within `unsafe` scope here? */ }

(The unsafe attribute docs appear to be silent on this question, at least AFAICT. Until reading this thread, I had not really contemplated the question and (naively?) assumed no unsafe scope-extension.)

If the answer is 'yes', then this is proposal is consistent and I'm in support. But if not, this seems inconsistent with other unsafe attributes and would represent a "wart" or "gotcha" (however small) in the language.

Thoughts?

@traviscross
Copy link
Contributor

traviscross commented Apr 18, 2025

I'm concerned though, that this may be inconsistent with other uses of unsafe attributes. Do all unsafe attributes extend the scope of their unsafe over the proceeding item? For example:

#[unsafe(no_mangle)]
pub fn example() { /* Are we within `unsafe` scope here? */ }

In a sense, yes, we are. If we write,

#[unsafe(no_mangle)]
pub extern "C" fn malloc(x: isize) -> *mut c_void { .. }

then whether the resulting program is valid is going to necessarily depend on the body of that function. Even if we don't use unsafe anywhere within that body, we still have a safety obligation to discharge that extends across the signature and the entire body (setting aside what might be our build obligations also).

It's similar here. Removing the function prelude, like stepping on malloc, binds together the function signature and its body into one safety obligation that we're discharging together.

@traviscross
Copy link
Contributor

traviscross commented Apr 18, 2025

On the procedurals here, @folkertdev, I've been watching with interest the dance we're doing in:

Once this is all settled, please give this PR a @rustbot ready.

@tgross35: Since you've been involved in those steps, I'd be interested in seeing your approving review on the implementation details of this PR.

And, @Amanieu, as always on asm-related matters, I'd be interested to see your approving review here as well.

Once it's ready, I'll give it a final look over myself and, if it has these approving reviews, give it the r= directly, or assign it off for that.

@tgross35
Copy link
Contributor

tgross35 commented Apr 18, 2025

I did take a thorough look at this before and it all seems correct to my knowledge. I'll re-review once #139753 lands, but I am very much +1 on having this stabilized.

The only usage comment I have is that it would be nice if we could #[cfg] within naked_asm! somehow so small platform-specific changes don't mean the entire function needs to be duplicated. Definitely something for after stabilization though, since it affects global_asm as well.

@folkertdev
Copy link
Contributor Author

I just found this

// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
// `#[naked]` attribute with just a lint, because we previously
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked")
}

There are similar comments on a number of other attributes, but is this something that could/should be fixed as we stabilize this attribute?

@traviscross
Copy link
Contributor

traviscross commented Apr 18, 2025

There are similar comments on a number of other attributes, but is this something that could/should be fixed as we stabilize this attribute?

Good catch. Yes. Specifically, this is a hard error currently (without the feature flag):

struct S {
    #[naked] //~ ERROR
    field: (),
}

I can't imagine that we'd want this to become allowed on stable due to us stabilizing naked_functions.

@traviscross
Copy link
Contributor

I'm surprised about some of the other places this exception is made also in the code. I can't imagine that we want it for #[marker] either, and I'd be surprised if any nightly users had actually relied on that or that we would particularly care even if they had. It and any other exceptions for unstable attributes seem likely to set up a stabilization hazard for us, and I'd be curious to hear if there are any reasons that all such cases shouldn't be removed.

@traviscross
Copy link
Contributor

cc #80564

jhpratt added a commit to jhpratt/rust that referenced this pull request Apr 19, 2025
…ttribute, r=tgross35,traviscross

Make `#[naked]` an unsafe attribute

tracking issue: rust-lang#138997

Per rust-lang#134213 (comment), the `#[naked]` attribute is now an unsafe attribute (in any edition).

This can only be merged when the above PRs are merged, I'd just like to see if there are any CI surprises here, and maybe there is early review feedback too.

r? `@traviscross`
@Amanieu
Copy link
Member

Amanieu commented Apr 19, 2025

The stabilization PR LGTM, so r=me.

@folkertdev folkertdev force-pushed the stabilize-naked-functions branch from 02cff1f to f087806 Compare April 19, 2025 14:24
@rustbot rustbot added the A-attributes Area: Attributes (`#[…]`, `#![…]`) label Apr 19, 2025
@rustbot
Copy link
Collaborator

rustbot commented Apr 19, 2025

Some changes occurred in compiler/rustc_passes/src/check_attr.rs

cc @jdonszelmann

ChrisDenton added a commit to ChrisDenton/rust that referenced this pull request Apr 19, 2025
…ttribute, r=tgross35,traviscross

Make `#[naked]` an unsafe attribute

tracking issue: rust-lang#138997

Per rust-lang#134213 (comment), the `#[naked]` attribute is now an unsafe attribute (in any edition).

This can only be merged when the above PRs are merged, I'd just like to see if there are any CI surprises here, and maybe there is early review feedback too.

r? ``@traviscross``
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Apr 20, 2025
Rollup merge of rust-lang#139753 - folkertdev:naked-function-unsafe-attribute, r=tgross35,traviscross

Make `#[naked]` an unsafe attribute

tracking issue: rust-lang#138997

Per rust-lang#134213 (comment), the `#[naked]` attribute is now an unsafe attribute (in any edition).

This can only be merged when the above PRs are merged, I'd just like to see if there are any CI surprises here, and maybe there is early review feedback too.

r? ``@traviscross``
@folkertdev folkertdev force-pushed the stabilize-naked-functions branch from f087806 to df8a3d5 Compare April 20, 2025 09:20
Copy link
Contributor Author

@folkertdev folkertdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe everything is in now, I've just rebased on master.

@rustbot ready

Thanks everyone for the help here!

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 20, 2025
Copy link
Contributor

@tgross35 tgross35 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as well

@greaka
Copy link
Contributor

greaka commented Apr 20, 2025

The only usage comment I have is that it would be nice if we could #[cfg] within naked_asm! somehow so small platform-specific changes don't mean the entire function needs to be duplicated.

Is there an issue to follow this specifically? It's already a problem when using global_asm.

@traviscross
Copy link
Contributor

Thanks for those reviews. Looks like we haven't quite finished reviewing the Reference PR, so we'll do that quickly and then move this along.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) A-run-make Area: port run-make Makefiles to rmake.rs disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. I-lang-nominated Nominated for discussion during a lang team meeting. needs-fcp This change is insta-stable, so needs a completed FCP to proceed. PG-exploit-mitigations Project group: Exploit mitigations S-waiting-on-documentation Status: Waiting on approved PRs to documentation before merging S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. to-announce Announce this issue on triage meeting
Projects
None yet
Development

Successfully merging this pull request may close these issues.