Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

arielb1
Copy link
Contributor

@arielb1 arielb1 commented Sep 9, 2025

Stabilize -Zstack-protector as -Cstack-protector

I propose stabilizing -Zstack-protector as -Cstack-protector. This PR adds a new -Cstack-protector flag, leaving the unstable -Z flag as is to ease the transition period. The -Z flag will be removed in the future.

Please hold technical discussion in the zulip thread at https://rust-lang.zulipchat.com/#narrow/channel/233931-t-compiler.2Fmajor-changes/topic/Proposal.20for.20Adapt.20Stack.20Protector.20for.20Ru.E2.80.A6.20compiler-team.23841

Let's move mitigation enforcement discussion to https://rust-lang.zulipchat.com/#narrow/channel/233931-t-compiler.2Fmajor-changes/topic/Mitigation.20enforcement.20.28.60-C.20allow-partial-sanitizer.60.29/with/539144305

-Zstack-protector stabilization report

What is the RFC for this feature and what changes have occurred to the user-facing design since the RFC was finalized?

No RFC/MCP, this flag was added in #84197 and was not deemed large enough to require additional process.

The tracking issue for this feature is #114903.

What behavior are we committing to that has been controversial? Summarize the major arguments pro/con.

The -Cstack-protector=strong mode uses the same underlying heuristics as Clang's -fstack-protector-strong.
These heuristics weren't designed for Rust, and may be over-conservative in some cases - for example, if
Rust stores a field's data in an alloca using an LLVM array type, LLVM regard the alloca as meaning
that the function has a C array, and enable stack overflow canaries even if the function accesses
the alloca in a safe way. Some people thought we should wait on stabilization until there are better
heuristics, but I didn't hear about any concrete case where this unduly harms performance, and I think
that when a need comes, we can improve the heuristics in LLVM after stabilization.

The heuristics do seem to not be under-conservative, so this should not be a security risk.

The -Cstack-protector=basic mode (-fstack-protector) uses heuristics that are specifically designed
to catch old-C-style string manipulation. This is not a good fit to Rust, which does not perform much
unsafe C-style string manipulation. As far as I can tell, nobody has been asking for it,
and few people are using it even in today's C - modern distros (e.g. Debian) tend to use
-fstack-protector-strong.

Therefore, -Cstack-protector=basic has been removed. If anyone is interested in it, they
are welcome to add it back as an unstable option.

Summarize the major parts of the implementation and provide links into the code (or to PRs)

Most implementation was done in #84197. The command-line
attribute enables the relevant LLVM attribute on all functions in

fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
let sspattr = match cx.sess().stack_protector() {
StackProtector::None => return None,
StackProtector::All => AttributeKind::StackProtectReq,
StackProtector::Strong => AttributeKind::StackProtectStrong,
StackProtector::Basic => AttributeKind::StackProtect,
};
Some(sspattr.create_attr(cx.llcx))
}
.

Each target can indicate that it does not support stack canaries - currently,
the GPU platforms nvptx64-nvidia-cuda and amdgcn-amd-amdhsa. On these
platforms, use of -Cstack-protector causes an error.

Summarize existing test coverage of this feature

The feature has tests that make sure that the LLVM heuristic gives reasonable
results for several functions, by checking for __security_check_cookie (on Windows)
or __stack_chk_fail (on Linux). See
https://github.com/rust-lang/rust/tree/68baa87ba6f03f8b6af2a368690161f1601e4040/tests/assembly-llvm/stack-protector

Has a call-for-testing period been conducted? If so, what feedback was received?

No call-for-testing has been conducted, but the feature seems to be in use.

What outstanding bugs in the issue tracker involve this feature? Are they stabilization-blocking?

No reported bugs seem to exist.

Summarize contributors to the feature by name for recognition and assuredness that people involved in the feature agree with stabilization

What FIXMEs are still in the code for that feature and why is it ok to leave them there?

No FIXMEs related to this feature.

What static checks are done that are needed to prevent undefined behavior?

This feature cannot cause undefined behavior.

In what way does this feature interact with the reference/specification, and are those edits prepared?

No changes to reference/spec, docs added to the codegen docs as part of the stabilization PR.

Does this feature introduce new expressions and can they produce temporaries? What are the lifetimes of those temporaries?

No.

What other unstable features may be exposed by this feature?

None.

What is tooling support like for this feature, w.r.t rustdoc, clippy, rust-analzyer, rustfmt, etc.?

No support needed for rustdoc, clippy, rust-analyzer, rustfmt or rustup.

Cargo could expose this as an option in build profiles but I would expect the decision as to what version should be used would
be made for the entire crate graph at build time rather than by individual package authors.

-C stack-protector is propagated to C compilers using cc-rs via rust-lang/cc-rs#1550

Fixes #114903

r? @wesleywiser (feel free to reassign)

@rustbot
Copy link
Collaborator

rustbot commented Sep 9, 2025

This PR modifies bootstrap.example.toml.

If appropriate, please update CONFIG_CHANGE_HISTORY in src/bootstrap/src/utils/change_tracker.rs.

These commits modify compiler targets.
(See the Target Tier Policy.)

Some changes occurred in tests/codegen-llvm/stack-protector.rs

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

Some changes occurred in src/doc/rustc/src/exploit-mitigations.md

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

Some changes occurred in tests/ui/stack-protector

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

@rustbot rustbot added A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. PG-exploit-mitigations Project group: Exploit mitigations S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 9, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 9, 2025

wesleywiser is currently at their maximum review capacity.
They may take a while to respond.

@rustbot

This comment has been minimized.

Comment on lines 292 to 295
The exact rules are unstable and subject to change, but
currently, it generates stack protectors for functions that,
*post-optimization*, contain either arrays (of any size
or type) or address-taken locals.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The exact rules are unstable and subject to change, but
currently, it generates stack protectors for functions that,
*post-optimization*, contain either arrays (of any size
or type) or address-taken locals.
The exact rules are unstable and subject to change, but
currently, it generates stack protectors for functions that,
*post-optimization*, contain stack allocations.

As in Rust all allocas are arrays. (Making this "address-taken stack allocations" would be the potential future improvement.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I suppose that "spill" stack allocations don't count? So stack allocations isn't exactly precise.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will say "contain stack allocations that are accessed in a way that's not precisely tracked by the compiler".

@davidtwco davidtwco removed the T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) label Sep 9, 2025
@davidtwco
Copy link
Member

@rfcbot fcp merge

See PR description for stabilisation report

@rust-rfcbot
Copy link
Collaborator

rust-rfcbot commented Sep 9, 2025

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

No concerns currently listed.

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!

See this document for info about what commands tagged team members can give me.

@rust-rfcbot rust-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 Sep 9, 2025
@rustbot rustbot added the T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) label Sep 9, 2025
@rustbot

This comment has been minimized.

determined by static control flow).
none
Do not generate stack canaries.
Copy link
Member

Choose a reason for hiding this comment

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

I know it's not widely used by Clang/Gcc, but should we add why the basic mode/strategy is not applicable to Rust and thus not available in case people look for it for matching build configurations?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that outside of rustc it's always called -fstack-protector (rather than "basic stack protection", which is not a name I've ever heard outside of a Rust context), but I agree that it makes sense to have documentation for why it does not exist. I think only in the markdown file, rather than in this file (tho I have no problem putting it in both places).

Copy link
Member

Choose a reason for hiding this comment

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

Yes, agreed. I actually meant mainly in the markdown docs, but left the comment in the wrong place. Something like: -fstack-protector or -fstack-protector=basic is not available/applicable to Rust because... (or equivalent to it). What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, will do that soon-ish.

@rcvalle
Copy link
Member

rcvalle commented Sep 10, 2025

Should this be a target modifier? (See #136966.) This exploit mitigation has mostly local properties and provide benefits when applied partially (but my recommendation is always to use/apply it thoroughly). It also doesn't cause any ABI changes or incompatibilities when used/applied partially so I'm unsure whether to make it a target modifier or not. Any thoughts?

@arielb1
Copy link
Contributor Author

arielb1 commented Sep 10, 2025

Should this be a target modifier?

As far as I can tell, target modifier should only be used for cases where differences in the target modifier can lead to unsoundness. With -C stack-protector, that is not the case - every function can have it's own stack canary settings.

And it's pretty common for code to be compiled together with different stack-protector settings - for example, for C code to be compiled with -fstack-protector-strong (or even -fstack-protector-all) while Rust is compiled with some other setting (even today's default, -C stack-protector=none).

I don't think that anyone expects to need to use -Z build-std to use -C stack-protector.

On the other hand, it is the case that in more modern mitigations, you generally expect them to be implemented for the entire program (theoretically, you could make -fstack-protector-strong be -fsanitize=stack-protector-strong and then you would expect sanitizer-like behavior). But I don't think anyone expects that.

Actually, thinking about this more, what we probably want is to have a separate -C enforce-stack-protector=strong/-C enforce-stack-protector=all option, that makes sure that every code you have in your object has (at least that) that stack protector enabled (possibly being disabled by a -C allow-partial-mitigations or however we call that option). We should add that option when we start adding denial of partial mitigations (annoyingly enough, it will be hard to make it look into C code, but it will at least help in pure-Rust contexts).

I believe -C enforce-stack-protector should be different from -C stack-protector since people use the -fstack-protector options in a mixed context all the time.

@rcvalle
Copy link
Member

rcvalle commented Sep 11, 2025

Should this be a target modifier?

As far as I can tell, target modifier should only be used for cases where differences in the target modifier can lead to unsoundness. With -C stack-protector, that is not the case - every function can have it's own stack canary settings.

Another use is when it may inadvertently lead to considerably reducing the effectiveness or make it considerably difficult to reason about the security guarantees/threat model of an exploit mitigation, the latter is which is not the case here. This exploit mitigation has local security guarantees/threat model that are relatively easy to reason about. However, inadvertently linking in unprotected compiled code could reduce the effectiveness of the exploit mitigation and security of the overall program, and thus why I asked about your thoughts about it.

And it's pretty common for code to be compiled together with different stack-protector settings - for example, for C code to be compiled with -fstack-protector-strong (or even -fstack-protector-all) while Rust is compiled with some other setting (even today's default, -C stack-protector=none).

I don't think that anyone expects to need to use -Z build-std to use -C stack-protector.

On the other hand, it is the case that in more modern mitigations, you generally expect them to be implemented for the entire program (theoretically, you could make -fstack-protector-strong be -fsanitize=stack-protector-strong and then you would expect sanitizer-like behavior). But I don't think anyone expects that.

Actually, thinking about this more, what we probably want is to have a separate -C enforce-stack-protector=strong/-C enforce-stack-protector=all option, that makes sure that every code you have in your object has (at least that) that stack protector enabled (possibly being disabled by a -C allow-partial-mitigations or however we call that option). We should add that option when we start adding denial of partial mitigations (annoyingly enough, it will be hard to make it look into C code, but it will at least help in pure-Rust contexts).

I believe -C enforce-stack-protector should be different from -C stack-protector since people use the -fstack-protector options in a mixed context all the time.

I mean, this is exactly the feature target modifiers provide. The user, upon their choosing, can override it with a flag (e.g., -Cunsafe-allow-abi-mismatch=stack-protector). We could provide some helpful error message like "Did you mean using...", and also explain the consequences/side effects of that. What do you think?

@arielb1
Copy link
Contributor Author

arielb1 commented Sep 11, 2025

We could provide some helpful error message like "Did you mean using...", and also explain the consequences/side effects of that. What do you think?

Nit: I thought that there is a widespread agreement to only use "unsafe" to indicate actual unsoundness as opposed to merely "undesirable consequences" - which is why I want the -C flag to be called something like -C allow-partial-mitigations rather than -C unsafe-allow-abi-mismatch.

The more significant issue, is that the way stack-protector is desired to be used today is as far as I can tell closer to partial than to full:

Basically, as far as I can tell, today, there is a fairly small number of security-critical projects (for example, the Linux kernel, web browsers) that expect to control everything that goes into their address space - these are also the people that tend to be using the "modern" sanitizer mitigations.

The other projects basically live with whatever they end up with. These normally don't use the "modern" mitigations. Trying to make stack-protector force them into a "whole program" mitigation world will just make them not use stack-protector.

Also, I think that even with the "modern" mitigations, if you want to have a chance to push them within an open world, you'll need to have a way for people to enable them opportunistically, because otherwise it will just be very hard to incrementally adopt them - anyone that has a build system would need to make a binary choice of whether to enable them or not, and in both cases they will be wrong.

This would mean that you might want to have separate -C sanitize=xyz and -C enforce-sanitize=xyz flags, even for the "modern" mitigations. This is somewhat less important for them, since currently they mostly seem to be used by people that have strong control over their compilation process - but if you ever wanted to get them widely adopted, you'll probably have to start with being able to use them in non-enforced mode.

The other direction is that -C stack-protector=strong is a fairly good default for anyone that cares about security - I can imagine Ubuntu enabling it by default. It increases security without harming performance much. On the other hand, -C enforce-stack-protector=strong is not something you'll want to enable by default, since it will make it very annoying to use your compiler in an "open-world" context.

@rcvalle
Copy link
Member

rcvalle commented Sep 12, 2025

We could provide some helpful error message like "Did you mean using...", and also explain the consequences/side effects of that. What do you think?

Nit: I thought that there is a widespread agreement to only use "unsafe" to indicate actual unsoundness as opposed to merely "undesirable consequences" - which is why I want the -C flag to be called something like -C allow-partial-mitigations rather than -C unsafe-allow-abi-mismatch.

Actually not. There are several examples in the approved RFC about exploit mitigations--I'd argue that the rationale for the RFC was mostly based on exploit mitigations. E.g.:

"For sanitizers used in production (such as shadow call stack or kcfi) this is particularly problematic, as a vulnerability in sanitized code may allow you to jump into unsanitized code."

The more significant issue, is that the way stack-protector is desired to be used today is as far as I can tell closer to partial than to full:

I'm not sure about that. Is it? Most major Linux distributions build their packages with stack protector strong, including the C standard library (which would be the equivalent of -Zbuild-std) and have the option implicitly enabled in their toolchains so the behavior the user gets is much closer to using -Zbuild-std than not.

Basically, as far as I can tell, today, there is a fairly small number of security-critical projects (for example, the Linux kernel, web browsers) that expect to control everything that goes into their address space - these are also the people that tend to be using the "modern" sanitizer mitigations.

The other projects basically live with whatever they end up with. These normally don't use the "modern" mitigations. Trying to make stack-protector force them into a "whole program" mitigation world will just make them not use stack-protector.

Also, I think that even with the "modern" mitigations, if you want to have a chance to push them within an open world, you'll need to have a way for people to enable them opportunistically, because otherwise it will just be very hard to incrementally adopt them - anyone that has a build system would need to make a binary choice of whether to enable them or not, and in both cases they will be wrong.

This would mean that you might want to have separate -C sanitize=xyz and -C enforce-sanitize=xyz flags, even for the "modern" mitigations. This is somewhat less important for them, since currently they mostly seem to be used by people that have strong control over their compilation process - but if you ever wanted to get them widely adopted, you'll probably have to start with being able to use them in non-enforced mode.

We wouldn't be forcing the user to apply the mitigation throughout we would just have more secure defaults and a better security posture (which is the culture behind the Rust compiler and community). We can be informative, but also give the option to the user to do otherwise if needed/wanted. This is not forcing the user, but helping the user to do the right thing.

I don't think we would also want to have an additional -C enforce- option for every exploit mitigation option we have, even less because we've created a feature to address exactly this and provides a better approach than replicating options.

The other direction is that -C stack-protector=strong is a fairly good default for anyone that cares about security - I can imagine Ubuntu enabling it by default. It increases security without harming performance much. On the other hand, -C enforce-stack-protector=strong is not something you'll want to enable by default, since it will make it very annoying to use your compiler in an "open-world" context.

It's actually enabled by default in Ubuntu for quite some time already1. As I mentioned above, not only for all packages built and shipped with the distribution, including the C standard library, but also implicitly enabled in the provided toolchain.

Footnotes

  1. https://wiki.ubuntu.com/ToolChain/CompilerFlags

@Darksonn
Copy link
Contributor

We could provide some helpful error message like "Did you mean using...", and also explain the consequences/side effects of that. What do you think?

Nit: I thought that there is a widespread agreement to only use "unsafe" to indicate actual unsoundness as opposed to merely "undesirable consequences" - which is why I want the -C flag to be called something like -C allow-partial-mitigations rather than -C unsafe-allow-abi-mismatch.

Actually not. There are several examples in the approved RFC about exploit mitigations--I'd argue that the rationale for the RFC was mostly based on exploit mitigations. E.g.:

"For sanitizers used in production (such as shadow call stack or kcfi) this is particularly problematic, as a vulnerability in sanitized code may allow you to jump into unsanitized code."

It is indeed the case that it can make sense to use target modifiers for exploit mitigations even if it strictly speaking isn't UB to mix them. See for example rust-lang/compiler-team#868, which says:

I suggest that the flags should utilize the target modifier infrastructure to prevent mixing compilation units with and without the flags because such misuse breaks the mitigation. However, the flag to opt-out from this check does not necessarily need the word "unsafe" because it's not actually part of the ABI.

However, I want to clarify that shadow call stack and kcfi are not examples of this. These two sanitizers can be part of an actual ABI issue. For example, last year I spent over a week debugging an ABI issue related to shadow-call-stack being mismatched between C and Rust CUs.

On the topic of -C allow-partial-mitigations, I do think that is a better name when target modifiers are used on an exploit mitigation rather than a real ABI mismatch.

@arielb1
Copy link
Contributor Author

arielb1 commented Sep 12, 2025

tl;dr I think the right "default for defaults" is -C stack-protector=strong -C allow-partial-mitigations=stack-protector.

It's actually enabled by default in Ubuntu for quite some time already1. As I mentioned above, not only for all packages built and shipped with the distribution, including the C standard library, but also implicitly enabled in the provided toolchain.

But it doesn't error if you try to use it with code that has different stack-protector flags.

For example, if you have code that suffers performance impact from stack protectors, you can set -fno-stack-protector for it, and you will still be protected from buffer overflows in other code.

For example, today, if you have Rust code that links against C code that is compiled on Ubuntu, the C code is likely to have stack protectors and the Rust code is likely to not, and AFAICT there are no plans for changing that - once we stabilize -C stack-protector, a later version of Ubuntu might pick it as the default for their Rust toolchain, but that's for the future - and will still not affect people that use rustup rs + the Ubuntu C compiler.

Of course, the current definition of target modifiers only applies to Rust code compiled within a single Rust "world", which means that, since (other than std) normally all Rust code within a single "world" is compiled by a single session of Cargo, there is much less reason for mismatches to happen, so forbidding them is less of a problem.

However, for the ABI target modifiers, if it was possible, it would make sense to extend them to deal with everything within the same address space (ABI mismatches between C and Rust are just as bad as ABI mismatches between Rust and Rust). That probably also makes sense for e.g. Spectre mitigations - it would be useful to have a programmatic way of making sure that all object files linked within a kernel have Spectre mitigations enabled - but for stack protectors, it is going to be continue to be the case that many times, code within an address space probably does not have a single stack-protector setting - which is more secure than what happens with the "modern" sanitizers, which is that in these "open world" cases, all code within the same address space has them in the same setting - disabled.

We wouldn't be forcing the user to apply the mitigation throughout we would just have more secure defaults and a better security posture (which is the culture behind the Rust compiler and community). We can be informative, but also give the option to the user to do otherwise if needed/wanted. This is not forcing the user, but helping the user to do the right thing.

I agree that -C stack-protector=strong is probably a good default to have. However, I think that making stack-protector "enforcing" would make it harder to have that as a default.

I don't think we would also want to have an additional -C enforce- option for every exploit mitigation option we have, even less because we've created a feature to address exactly this and provides a better approach than replicating options.

For mitigations that make sense to enable partially (not e.g. Spectre mitigations - if you have a single reachable function in your code that is vulnerable to Spectre, attackers can exploit it even if it doesn't have a bug, which makes having Spectre mitigations in only a part of the code basically useless), having a separate -C enforce option makes it much easier to deploy them incrementally. Which I think makes it much easier to end up deploying them completely, after a period of time (you can start with non-enforcing mode, iron out the bugs, then enable enforcing mode).

In some sense, this is bikeshedding, but I think that the right "default for defaults" would be -C stack-protector=strong -C allow-partial-mitigations=stack-protector. Except that I think it's better for allow-partial-mitigations to be "negative sense", because then end-users can override the users by enabling enforcement (you can allow end-users to do -C allow-partial-mitigations=stack-protector=false, but meh. I am actually fine with that, I don't care what color the bikeshed is, well except that I don't like having the word "unsafe" within the bikeshed color, because people don't like having "unsafe" in defaults for understandable reasons).

@arielb1
Copy link
Contributor Author

arielb1 commented Sep 12, 2025

Looking more at things - the -C unsafe-allow-abi-mismatch (whatever name you give it) is supposed only to be used in the "I really know what I am doing, it's actually fine" case, and it basically marks the crate as if it had a "wildcard" ABI.

I don't think this is the right affordance for stack protector. I think that for stack protector, disabling it is more of a case of "I care more about performance than about security". In which case, I think the right affordance is that the application compiler - the leaf caller of rustc - should be the one that is aware of the performance/security tradeoffs their dependencies are making.

I don't want people that are trying to fix their compile errors to end up using the "I really know what I am doing, it's actually fine" flag.

@arielb1
Copy link
Contributor Author

arielb1 commented Sep 12, 2025

Maybe it's better if we just make -C stack-protector=strong -C allow-partial-mitigations=stack-protector the "default", and then have a separate tool to do the enforcement, perhaps with some allowlist of crates that are allowed to skip mitigations?

Or we could have rustc as that tool. In any case that can be further work since nobody wants it now.

@rcvalle
Copy link
Member

rcvalle commented Sep 12, 2025

We could provide some helpful error message like "Did you mean using...", and also explain the consequences/side effects of that. What do you think?

Nit: I thought that there is a widespread agreement to only use "unsafe" to indicate actual unsoundness as opposed to merely "undesirable consequences" - which is why I want the -C flag to be called something like -C allow-partial-mitigations rather than -C unsafe-allow-abi-mismatch.

Actually not. There are several examples in the approved RFC about exploit mitigations--I'd argue that the rationale for the RFC was mostly based on exploit mitigations. E.g.:
"For sanitizers used in production (such as shadow call stack or kcfi) this is particularly problematic, as a vulnerability in sanitized code may allow you to jump into unsanitized code."

It is indeed the case that it can make sense to use target modifiers for exploit mitigations even if it strictly speaking isn't UB to mix them. See for example rust-lang/compiler-team#868, which says:

I suggest that the flags should utilize the target modifier infrastructure to prevent mixing compilation units with and without the flags because such misuse breaks the mitigation. However, the flag to opt-out from this check does not necessarily need the word "unsafe" because it's not actually part of the ABI.

However, I want to clarify that shadow call stack and kcfi are not examples of this. These two sanitizers can be part of an actual ABI issue. For example, last year I spent over a week debugging an ABI issue related to shadow-call-stack being mismatched between C and Rust CUs.

Thanks, @Darksonn! I was quoting the part of where the risk of reducing the effectiveness of the exploit mitigation and security of the overall program are also mentioned as rationale. I know you had some ABI issues as well for these. For example, for CFI, even though there are no ABI issues, I'm intending to make it a target modifier entirely because of the risk of reducing the effectiveness of the exploit mitigation and security of the overall program.

On the topic of -C allow-partial-mitigations, I do think that is a better name when target modifiers are used on an exploit mitigation rather than a real ABI mismatch.

The more I think about it, I think we should either:

  1. Make stack smashing protector a target modifier (and maybe use -C allow-partial-mitigations=stack-protector to allow it to be applied partially).
  2. Do (1) and enable it with the strong mode/strategy by default both for the Rust standard library and the Rust compiler.

For both (1) and (2), adding the -C allow-partial-mitigations option has the benefit we can use it for other exploit mitigations we're planning to make target modifiers as well.

For (2), we could perform a comprehensive set of tests for binary size, build time, and run time performance and make a decision based upon the ROI.

@arielb1
Copy link
Contributor Author

arielb1 commented Sep 12, 2025

  1. Do you think that an insta-stable PR for (1) [stack-protector, with -C allow-partial-mitigations=stack-protector] is a good enough idea?
  2. I think that we should default -Z stack-protector to be -C stack-protector -C allow-partial-mitigations=stack-protector, to reduce the amount of upgrade pain that people feel (since otherwise people have no easy way of creating code that works on both compiler versions). I think it would be right to get rid of the old -Z stack-protector after basically an edition / a few years.

@Noratrieb
Copy link
Member

Noratrieb commented Sep 12, 2025

I would suggest to split out the discussion around the behavior of stack protector enforcement and figure out what to best do there on a separate thread instead of the stabilization here. We generally want to avoid such larger discussions on stabilizations, because stabilizations are supposed to just be the final sign-off. It was definitely good that the issue came up, but I think now it's time to move it out.

From reading the comments, @arielb1's suggestion around not making it a target modifier and having some sort of -Callow-partial-mitigations seems like the most reasonable option to me, but I'm not in the space so I am not sure about this at all.

From a stabilization POV, I see two options really:

  • stabilize it with allowing partial mitigations (with no possibility for enforcement yet, and looking at mitigation enforcement holistically in the future), locking us out of a block-partial-by-default solution!
  • block stack protector stabilization on figuring out mitigation enforcement more generally

As mentioned, I'd suggest having a more detailed discussion about the advantages and use cases of mitigation enforcement in another place than the stabilization PR here (for example a separate issue or a Zulip thread).

@arielb1
Copy link
Contributor Author

arielb1 commented Sep 12, 2025

I would suggest to cancel the FCP and split out the discussion around the behavior of stack protector enforcement and figure out what to best do there on a separate thread instead of the stabilization here

There is a zulip thread https://rust-lang.zulipchat.com/#narrow/channel/233931-t-compiler.2Fmajor-changes/topic/Proposal.20for.20Adapt.20Stack.20Protector.20for.20Ru.E2.80.A6.20compiler-team.23841/with/538305719. We can talk on it

It's linked from the stabilization comments, but not loudly enough.

@Noratrieb
Copy link
Member

@rfcbot concern mitigation-enforcement

@arielb1
Copy link
Contributor Author

arielb1 commented Sep 12, 2025

@bors
Copy link
Collaborator

bors commented Sep 13, 2025

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

Ariel Ben-Yehuda added 6 commits September 13, 2025 15:49
I propose stabilizing `-Cstack-protector` as `-Zstack-protector`. This PR adds a new `-Cstack-protector` flag, leaving the unstable `-Z` flag as is to ease the transition period. The `-Z` flag will be removed in the future.

No RFC/MCP, this flag was added in 84197 and was not deemed large enough to require additional process.

The tracking issue for this feature is 114903.

The `-Cstack-protector=strong` mode uses the same underlying heuristics as Clang's `-fstack-protector-strong`.
These heuristics weren't designed for Rust, and may be over-conservative in some cases - for example, if
Rust stores a field's data in an alloca using an LLVM array type, LLVM regard the alloca as meaning
that the function has a C array, and enable stack overflow canaries even if the function accesses
the alloca in a safe way. Some people thought we should wait on stabilization until there are better
heuristics, but I didn't hear about any concrete case where this unduly harms performance, and I think
that when a need comes, we can improve the heuristics in LLVM after stabilization.

The heuristics do seem to not be under-conservative, so this should not be a security risk.

The `-Cstack-protector=basic` mode (`-fstack-protector`) uses heuristics that are specifically designed
to catch old-C-style string manipulation. This is not a good fit to Rust, which does not perform much
unsafe C-style string manipulation. As far as I can tell, nobody has been asking for it,
and few people are using it even in today's C - modern distros (e.g. [Debian]) tend to use
`-fstack-protector-strong`.

Therefore, `-Cstack-protector=basic` has been **removed**. If anyone is interested in it, they
are welcome to add it back as an unstable option.

[Debian]: https://wiki.debian.org/Hardening#DEB_BUILD_HARDENING_STACKPROTECTOR_.28gcc.2Fg.2B-.2B-_-fstack-protector-strong.29

Most implementation was done in <rust-lang#84197>. The command-line
attribute enables the relevant LLVM attribute on all functions in
<https://github.com/rust-lang/rust/blob/68baa87ba6f03f8b6af2a368690161f1601e4040/compiler/rustc_codegen_llvm/src/attributes.rs#L267-L276>.

Each target can indicate that it does not support stack canaries - currently,
the GPU platforms `nvptx64-nvidia-cuda` and `amdgcn-amd-amdhsa`. On these
platforms, use of `-Cstack-protector` causes an error.

The feature has tests that make sure that the LLVM heuristic gives reasonable
results for several functions, by checking for `__security_check_cookie` (on Windows)
or `__stack_chk_fail` (on Linux). See
<https://github.com/rust-lang/rust/tree/68baa87ba6f03f8b6af2a368690161f1601e4040/tests/assembly-llvm/stack-protector>

No call-for-testing has been conducted, but the feature seems to be in use.

No reported bugs seem to exist.

- bbjornse was the original implementor at 84197
- mrcnski documented it at 111722
- wesleywiser added tests for Windows at 116037
- davidtwco worked on the feature at 121742
- nikic provided support from the LLVM side (on Zulip on <https://rust-lang.zulipchat.com/#narrow/channel/233931-t-compiler.2Fmajor-changes/topic/Proposal.20for.20Adapt.20Stack.20Protector.20for.20Ru.E2.80.A6.20compiler-team.23841> and elsewhere),
  thanks nikic!

No FIXMEs related to this feature.

This feature cannot cause undefined behavior.

No changes to reference/spec, docs added to the codegen docs as part of the stabilization PR.

No.

None.

No support needed for rustdoc, clippy, rust-analyzer, rustfmt or rustup.

Cargo could expose this as an option in build profiles but I would expect the decision as to what version should be used would
be made for the entire crate graph at build time rather than by individual package authors.

`-C stack-protector` is propagated to C compilers using cc-rs via rust-lang/cc-rs issue 1550
stack-protector=basic is a heuristic designed for C

putting this in its own commit to allow for easy reversion if someone wants it
@rustbot
Copy link
Collaborator

rustbot commented Sep 13, 2025

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@arielb1
Copy link
Contributor Author

arielb1 commented Sep 13, 2025

Added the comment about stack-protector=basic. I expect to need these even after we stabilize the mitigaiton enforcement RFC.

@arielb1 arielb1 changed the title Stabilize stack protector Stabilize stack-protector Sep 13, 2025
@arielb1
Copy link
Contributor Author

arielb1 commented Sep 13, 2025

rust-lang/rfcs#3855

@SparrowLii
Copy link
Member

SparrowLii commented Sep 15, 2025

I believe the discussion on mitigation enforcement is highly meaningful. On the other hand, I hope we can reach a conclusion on the stabilization PR for stack-protector as soon as possible.
@Noratrieb @arielb1 How far are we from reaching it now?

@Noratrieb
Copy link
Member

My reading is that the consensus between the interested people is that enforcement should be the default, therefore this is now blocked on that. If you disagree and would like to see this stabilized without enforcement by not making it the default, you are welcome to make your case on the Zulip thread and the enforcement RFC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. PG-exploit-mitigations Project group: Exploit mitigations proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Tracking Issue for stabilizing stack smashing protection (i.e., -Z stack-protector)