Repository navigation
RFC: unsafe global_asm
#4014
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
base: main
Are you sure you want to change the base?
RFC: unsafe global_asm
#4014
Changes from all commits
bb600d2
dddfbbb
4dd8c9f
26f7c95
4f515bb
f1c60b2
eb78e5e
35adb44
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| - Feature Name: `unsafe_global_asm` | ||
| - Start Date: 2026-10-06 | ||
| - RFC PR: [rust-lang/rfcs#4014](https://github.com/rust-lang/rfcs/pull/4014) | ||
| - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) | ||
|
|
||
| ## Summary | ||
| [summary]: #summary | ||
|
|
||
| Require `unsafe` for usage of the built-in `global_asm` macro. | ||
|
|
||
| ## Motivation | ||
| [motivation]: #motivation | ||
|
|
||
| The `global_asm` macro can be used to cause undefined behaviour by overwriting symbols. | ||
| The use of this macro is currently not marked as unsafe in any way. | ||
| New syntax is required to close this unsoundness, because it is currently not possible to mark a macro | ||
| invocation as unsafe. | ||
|
|
||
| ## Guide-level explanation | ||
| [guide-level-explanation]: #guide-level-explanation | ||
|
|
||
| When declaring a function in a `global_asm` macro like this | ||
|
|
||
| ```rust | ||
| global_asm!(" | ||
| .globl write | ||
| write: | ||
| ret | ||
| "); | ||
| ``` | ||
| it will cause Rust to generate a globally visible function with the linker name `write`. | ||
| Code that wants to call the POSIX `write` function might call this one instead. | ||
| This can lead to UB if: | ||
| - the assembly does not follow the ABI of the `write` function correctly. | ||
| - the assembly behaves in a way that is incompatible with the contract of the `write` function. | ||
|
|
||
| To avoid this `global_asm` must not declare functions or globals whose names clash with other functions or globals. | ||
| Since the compiler in general cannot check this it must be done by the programmer. | ||
| The syntax for discharging the safety obligation is the same as the syntax for unsafe trait implementations, where the `unsafe` | ||
| is also used to mark the safety obligations. | ||
|
|
||
| ```rust | ||
| /// SAFETY: there is no other global function named `my_own_write`. No other symbols are declared. | ||
| unsafe global_asm!(" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be an attribute EDIT: Missed the bullet point in the alternatives section. Could you expand on why this is more preferable than the attribute? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was mentioned in the Zulip thread which discussed a lot of threads. I think it will be good to link that thread in the RFC.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't feel like an #[unsafe]
#[no_mangle]
fn my_write() {}which i feel like is actually the equivalent to requiring an unsafe attribute here. In general i don't have that strong opinions on the syntax, so i found it hard to really argue against the others in the RFC, but i have some arguments that may be helpful and i can try to include the most convincing in the rfc.
Do you think so? i think in the syntax alternatives i mentioned every proposed alternative and i would favor summarizing arguments in favor or against syntax alternatives rather than linking to them. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please read the Zulip thread (and link it). This was already discussed there.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have read (and participated in) the zulip thread. I added an argument against There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Linking previous discussions (in "Prior Art") is important in RFCs even if you summarize them perfectly. I have not compared the thread and the RFC closely, but you do for example not mention the argument @ds84182 mentioned here, nor the response ( |
||
| .globl my_own_write | ||
| my_own_write: | ||
| ret | ||
| "); | ||
| ``` | ||
|
|
||
| ## Reference-level explanation | ||
| [reference-level-explanation]: #reference-level-explanation | ||
|
|
||
|
inkreasing marked this conversation as resolved.
|
||
| The `global_asm` macro is considered unsafe and (starting from the next edition) can only be invoked if the `unsafe` keyword appears | ||
| immediately before it. | ||
| Other macros can't be invoked in this way. | ||
|
|
||
| ```rust | ||
| unsafe global_asm!(""); // ERROR now | ||
| global_asm!(""); // ERROR on next edition | ||
| unsafe my_macro!(); // ERROR now and on next edition | ||
| ``` | ||
|
|
||
| To keep backwards compatibility using the `global_asm` macro without `unsafe` on older editions is not | ||
| a hard error, but is linted against. | ||
|
|
||
| The (currently unimplemented) feature [`global_asm_in_statement_position`](https://github.com/rust-lang/rust/issues/156965) | ||
| allows using `global_asm` in more positions. It will be adjusted to follow the same syntax laid out here. | ||
| The `unsafe` keyword is required, even if `global_asm` is invoked in an unsafe block. | ||
| ```rust | ||
| fn outer() { | ||
| unsafe { | ||
| global_asm!(""); // ERROR | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Because `global_asm` is an item and not a statement this behaviour is the same as other items (unsafe trait | ||
| implementations and functions with unsafe attributes). | ||
|
|
||
| The syntax of [MacroItem](https://doc.rust-lang.org/nightly/reference/items.html#grammar-MacroItem) is changed to the following: | ||
| ```grammar,items | ||
| MacroItem -> | ||
| `unsafe`? MacroInvocationSemi | ||
| | MacroRulesDefinition | ||
| ``` | ||
|
|
||
| Using `unsafe` for macros in non-item positions is still disallowed in the grammar. | ||
|
|
||
| ## Drawbacks | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this is a very lukewarm drawback, but it is worth mentioning that, right now, the set of unsafeties in code can be determined with a simple fixed-string search, and this would technically break this in general for macros, although would work fine if Right now, with rust-lang/rust#163781, the set of strings we need to search to find unsafe code (which has been formatted by
With just this change, we add:
But in general, we add the regular expression:
Note that this is only a lukewarm justification because the proposal for unsafe derives would require full name resolution and checking inside derive macros and in general we should probably not rely on this. Right now, tidy being based upon a simple search is extremely brittle, as shown by the fact that I'm just working on adding unsafe impls and attributes now. But, it is worth noting that this syntax is unique enough in Rust to be weird. The fact that it doesn't matter whether you're in an unsafe block is noteworthy to point out, although I think it would need something like this in general since macros can trivially "break out" of unsafe blocks: macro_rules! malicious {
() => {
fn hidden_scope(x: *const u32) -> u32 {
// this is not covered by the containing `unsafe { ... }` block
*x
}
hidden_scope()
}
}
fn overt_scope() {
unsafe {
malicious!();
}
}There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's already not true because you need to consider comments and strings. The only way to do it reliably is to over-approximate and search for
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't change your argument but you also need to add
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may be a weak drawback, but it is one, so i added it to the list. |
||
| [drawbacks]: #drawbacks | ||
|
|
||
| - Disallowing the old syntax is a breaking change. | ||
| - It makes `global_asm` more special since users can't require the use of `unsafe` for their own macros. | ||
| - If unsafe macros would ever be allowed the syntax should be consistent, so this limits the possibilities. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drawback: it doesn't really change anything other than making soundness risks more obvious, debatably. Making everyone add a keyword isn't likely to make more or fewer people use
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is a drawback? To be clear i completely agree that this won't change how of how often people use From a quick search on github i haven't seen a single safety comment on |
||
| - This syntax is harder to search for than the currently existing markers for unsafe code. | ||
|
|
||
| ## Rationale and alternatives | ||
| [rationale-and-alternatives]: #rationale-and-alternatives | ||
|
|
||
| - **Do Nothing**: This unsoundness stays open. | ||
| As [RFC 3324](https://github.com/rust-lang/rfcs/blob/master/text/3325-unsafe-attributes.md) argues, having the `unsafe_code` lint warn against this is not enough, | ||
| because the lint is opt-in, while Rust has "safety by default". | ||
| - **Disallow the old syntax on all editions**: This would break all Rust code that uses `global_asm`. | ||
| - **Other syntax**: There are a couple of alternatives. | ||
| - It could be renamed to something like `unsafe_global_asm`. This is less helpful when the macro is imported with a different name. | ||
| - It could require `unsafe` as part of its syntax (`global_asm!(unsafe {".."})` or `global_asm!(unsafe "..")`). | ||
| - Unsafe blocks in item position could be added. This is a big change that would require it's own RFC. I don't think `global_asm` is enough motivation for this. | ||
| - It could require an `#[unsafe]` or `#[unsafe()]` attribute, which wouldn't require the grammar of Rust to change. This syntax implies maybe even more generality, because one could expect `#[unsafe] impl Send for MyType {}` to work. | ||
|
|
||
| The chosen syntax mirrors the one chosen for unsafe attributes in that it does not rename the problematic | ||
| item and in that it defines a syntax for unsafe macros. | ||
|
Comment on lines
+108
to
+112
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With this RFC's proposed syntax we would have 3 ways to consume/assert unsafety,
I think this suggests, if unsafe macros were to exist, they should be restricted to produce items only (unlike the suggestion in line 141), to avoid further confusion (whether we should write Footnotes
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that unsafe macros on expression position should just require being put inside an
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the discussions about unsafe macros in expression positions and if unsafe macros are only allowed to produce items are out of scope for this RFC, since i don't think this limits any direction that could be taken. |
||
|
|
||
| ## Prior art | ||
| [prior-art]: #prior-art | ||
|
|
||
| Especially [RFC 3324](https://github.com/rust-lang/rfcs/blob/master/text/3325-unsafe-attributes.md), which made `no_mangle` | ||
| and similar attributes require `unsafe` is very similar to this RFC. | ||
| In both cases | ||
| - the unsoundness is related to symbols of items. | ||
| - the problematic feature was stabilised a long time ago. | ||
| - new syntax had to be added to mark the unsafety. | ||
| - the new syntax could be extended to unsafe macros in the future. | ||
| - the old syntax is only disallowed on a new edition. | ||
|
|
||
| The `unsafe_code` lint currently already fires on uses of `global_asm` with the note: | ||
| "using this macro is unsafe even though it does not need an `unsafe` block". | ||
| Something that is considered unsafe, but does not require an unsafe annotation to use is not a useful | ||
| concept. That `unsafe` marks *every* part of the code that requires special care is a big part of rusts safety story. | ||
|
|
||
| ## Unresolved questions | ||
| [unresolved-questions]: #unresolved-questions | ||
|
|
||
| - While this syntax seems the most sensible to me i am not opposed to another syntax. | ||
| - **Rollout of the lint / if this should be done across all editions**: | ||
| I don't see much advantage of breaking this code on older editions. | ||
|
|
||
| ## Future possibilities | ||
| [future-possibilities]: #future-possibilities | ||
|
|
||
| Users could be allowed to require `unsafe` for their own macros, which would at least partially reuse this syntax. | ||
| This RFC implies that it should be possible to invoke unsafe macros in item positions and that this should use the `unsafe my_macro!();` syntax. | ||
| Otherwise all directions, especially in regard to interaction with unsafe blocks stay open. | ||
|
|
||
| - If unsafe macros in expression positions should use the same syntax or require an `unsafe` block can be decided later. | ||
| - If unsafe macros should have to either expand to items or statements, but not both to differentiate between having to use an unsafe block or `unsafe my_macro!()` | ||
| can be decided later. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.