-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Mitigation enforcement #3855
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: master
Are you sure you want to change the base?
Mitigation enforcement #3855
Changes from 1 commit
1305c42
badc6b0
3f7188b
7cff7a6
a4c7653
bc908e3
cbd4202
a8abe47
cde2d04
5c02a45
6c00bc2
6981503
6862fc1
5cb85ae
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,347 @@ | ||
| - Feature Name: `mitigation_enforcement` | ||
| - Start Date: 2025-09-13 | ||
| - RFC PR: [rust-lang/rfcs#3855](https://github.com/rust-lang/rfcs/pull/3855) | ||
| - Rust Issue: None | ||
|
|
||
| # Summary | ||
| [summary]: #summary | ||
|
|
||
| Introduce the concept of "mitigation enforcement", so that when compiling | ||
| a crate with mitigations enabled (for example, `-C stack-protector`), | ||
| a compilation error will happen if the produced artifact would contain Rust | ||
| code without the same mitigations enabled. | ||
|
|
||
| This in many cases would require use of `-Z build-std`, since the standard | ||
| library only comes with a single set of enabled mitigations per target. | ||
|
|
||
| Mitigation enforcement should be disableable by the end-user via a compiler | ||
| flag. | ||
|
|
||
| # Motivation | ||
| [motivation]: #motivation | ||
|
|
||
| Memory unsafety mitigations are important for reducing the chance that a vulnerability | ||
| ends up being exploitable. | ||
|
|
||
| While in Rust, memory unsafety is less of a concern than in C, mitigations are | ||
| still important for several reasons: | ||
|
|
||
| 1. Some mitigations (for example, straight line speculation mitigation, | ||
| [`-Z harden-sls`]) mitigate the impact of Spectre-style speculative | ||
| execution vulnerabilities, that exist in Rust just as well as C. | ||
| 2. Many Rust programs also contain large C/C++ components, that can have | ||
wesleywiser marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| memory vulnerabilities. | ||
| 3. Many Rust programs use `unsafe`, that can introduce memory unsafety | ||
| and vulnerabilities. | ||
|
|
||
| Mitigations are generally enabled by passing a flag to the compiler (for | ||
| example, [`-Z harden-sls`] or [`-Z stack-protector`]). If the compilation | ||
|
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. You always talk about stack-protector here, since that's your primary motivation of course, but I think it would be good to more explicitly list out all kinds of mitigations that Rust has it that people would like Rust to have in the future, to ensure that this makes sense for all of them (for example, the ones from https://doc.rust-lang.org/nightly/rustc/exploit-mitigations.html#exploit-mitigations-1). I would especially be interested in whether there are existing stable mitigations that would like to make use of this, especially if it has a flag to toggle it. 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. Some exploit mitigations that would benefit from it (e.g., CFI, and including most in the https://doc.rust-lang.org/nightly/rustc/exploit-mitigations.html#exploit-mitigations-1) precedes the Target Modifiers feature (which was intended to also solve this), but I don't think there are any stable exploit mitigations except maybe 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. Done |
||
| process of a program is complex, it is very easy to end up accidentally | ||
| not passing the flag to one of the constituent object files. | ||
|
|
||
| This can have one of several consequences: | ||
|
|
||
| 1. In some cases (for example `-Z fixed-x18 -Z sanitizer=shadow-call-stack`), | ||
| the mitigation changes the ABI, and linking together code with different | ||
| mitigation settings leads to undefined behavior such as crashes even | ||
| in the absence of an attack. In these cases, the sanitizer should be a | ||
| [target modifier] rather than using this RFC. | ||
| 2. For "Spectre-type" mitigations (e.g. `harden-sls`), if there is some reachable | ||
| code in your address space without a retpoline, attackers can execute a | ||
| Spectre attack, even if there is 0 UB in your code. | ||
| 3. For "CFI-type" mitigations (e.g. kcfi), if there is reachable code in your | ||
| address space that does not have that sanitizer enabled, attackers can use it to | ||
| leverage an already-existing memory vulnerability into ROP execution, even | ||
| if the memory vulnerability is in a completely different part of the code than | ||
| the part that has the mitigation disabled | ||
| 4. For "local" mitigations (e.g. stack protector, or C's `-fwrapv` - which I don't think | ||
|
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. The equivalent of 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 not possible to have the Rust equivalent of
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 understand it doesn't quite fit the model here, but I've written code where we wanted to panic rather than wrapping, because wrapping meant we'd generated an incorrect value. I've also written code where we relied on the guaranteed wrapping behaviour. 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'll say this (enforcing for overflow checks) is off scope for the current RFC. There is no reason to do it in the "main" enforcing pulse. Feel free to open another RFC/FCP/whatever. 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 agree that it's not really a security mitigation in the classic sense and shouldn't be enforced. FWIW, relying on wrapping behavior is incorrect, as Rust always considers overflow to be a bug, it just doesn't always check it. Use wrapping if you need wrapping. |
||
| Rust has), the mitigation protects the code when it is in the right place | ||
| relative to the bug - a stack protector helps basically when it protects the buffer | ||
| that overflows, and it does not matter which other functions have a stack protector. | ||
|
|
||
| To avoid these consequences, teams that write software with high security needs - for | ||
| example, browsers and the Linux kernel - need to have a way to make sure that the | ||
| programs they produce have the mitigations they want enabled. | ||
|
|
||
| On the other hand, for teams that write software in a more messy environment, it | ||
| can be hard to chase down all dependencies, and especially for "local" mitigations, | ||
| being able to enable them on an object-by-object basis is the only thing that allows | ||
| for the mitigations to actually be deployed. Especially important is progressive | ||
| deployment - it's much easier to introduce mitigations 1 crate at a time than | ||
| to introduce mitigations a whole program at a time, even if the end goal is | ||
| to introduce the mitigations to the entire program. | ||
|
|
||
| [target modifier]: https://github.com/rust-lang/rfcs/pull/3716 | ||
| [`-Z harden-sls`]: https://github.com/rust-lang/compiler-team/issues/869 | ||
| [`-Z stack-protector`]: https://github.com/rust-lang/rust/issues/114903 | ||
| [example by Alice Ryhl]: https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler/topic/Target.20modifiers.20and.20-Cunsafe-allow-abi-mismatch/near/483871803 | ||
|
|
||
| # Guide-level explanation | ||
| [guide-level-explanation]: #guide-level-explanation | ||
|
|
||
| When you use a mitigation, such as `-C stack-protector=strong`, if one of your | ||
| dependencies does not have that mitigation enabled, compilation will fail. | ||
|
|
||
| > Error: your program uses the crate `foo`, that is not protected by | ||
| > `-C stack-protector=strong`. | ||
| > | ||
| > Recompile that crate with the mitigation enabled, or use | ||
| > `-C stack-protector=strong-noenforce` to allow creating an artifact | ||
| > that has the mitigation only partially enabled. | ||
|
|
||
| # Reference-level explanation | ||
| [reference-level-explanation]: #reference-level-explanation | ||
|
|
||
| Every flag value that enables a mitigation for which enforcement is desired is split | ||
| into 2 separate values, "enforcing" and "non-enforcing" mode. The enforcing mode | ||
| is the default, non-enforcing mode is constructed by adding `-noenforce` to the | ||
| name of the value, for example `-C stack-protector=strong-noenforce` or | ||
| `-C sanitizer=shadow-call-stack-noenforce`. | ||
|
|
||
| > It is possible to bikeshed the exact naming scheme. | ||
|
|
||
| > Every new mitigation would need to decide whether it adopts this scheme, | ||
| > but mitigations are expected to adopt it. | ||
|
|
||
| Every crate gets a metadata field that contains the set of mitigations it has enabled. | ||
|
|
||
| When compiling a crate, if the current crate has a mitigation with enforcement | ||
| turned on, and one of the dependencies does not have that mitigation turned | ||
| on (whether enforcing or not), a compilation error results. | ||
|
|
||
| If a mitigation has multiple "levels", a lower level at a child crate is compatible | ||
| with a higher level at a base crate. | ||
|
|
||
| The error happens independent of the target crate type (you get an error | ||
| if you are building an rlib, not just the final executable). | ||
|
|
||
| For example, with `-C stack-protector`, the compatibility table will be | ||
| as follows: | ||
|
|
||
| | Base\Child | none | none-noenforce | strong | strong-noenforce | all | all-noenforce | | ||
arielb1 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| | ---------------- | ---- | -------------- | ------ | -------------------- | ----- | -------------------- | | ||
| | none | OK | OK | error | OK - child noenforce | error | OK - child noenforce | | ||
| | none-noenforce | OK | OK | error | OK - child noenforce | error | OK - child noenforce | | ||
| | strong | OK | OK | OK | OK | error | OK - child noenforce | | ||
| | strong-noenforce | OK | OK | OK | OK | error | OK - child noenforce | | ||
| | all | OK | OK | OK | OK | OK | OK | | ||
| | all-noenforce | OK | OK | OK | OK | OK | OK | | ||
|
|
||
| If a program has multiple flags of the same kind, the last flag wins, so e.g. | ||
| `-C stack-protector=strong-noenforce -C stack-protector=strong` is the same as | ||
| `-C stack-protector=strong`. | ||
|
|
||
| # Drawbacks | ||
| [drawbacks]: #drawbacks | ||
|
|
||
| The `-noenforce` syntax is ugly, and the | ||
| `-C allow-partial-mitigations=stack-protector` syntax is either order-dependent | ||
| or does not allow for easy appending. | ||
|
|
||
| # Rationale and alternatives | ||
| [rationale-and-alternatives]: #rationale-and-alternatives | ||
|
|
||
| ## Syntax alternatives | ||
|
|
||
| ### -C stack-protector=none-noenforce | ||
|
|
||
| The option `-C stack-protector=none-noenforce` is the same as | ||
| `-C stack-protector=none`. I am not sure whether we should have both, but | ||
| it feels that orthogonality is in favor of having both. | ||
|
|
||
| ### -C allow-partial-mitigations | ||
|
|
||
| Instead of having `-C stack-protector=strong-noenforce`, we could have the | ||
| syntax be `-C stack-protector=strong -C allow-partial-mitigations=stack-protector`. | ||
|
|
||
| Some people feel that syntax is prettier. In that case, we have 2 options: | ||
|
|
||
| #### Without order dependency | ||
|
|
||
| This is the simplest to implement. With that, | ||
| `-C stack-protector=strong -C allow-partial-mitigations=stack-protector -C stack-protector=strong` | ||
| is the same as `-C stack-protector=strong -C allow-partial-mitigations=stack-protector`. | ||
|
|
||
| This is unfortunate, because `-C stack-protector=strong -C allow-partial-mitigations=stack-protector` is | ||
| a pretty good default for distributions to set. If a distribution sets that, and an application | ||
|
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. There's some earlier discussion of "distributors setting flags by default". To my knowledge, there is no out of the box mechanism that supports this. It would be helpful to explain what you mean by this. Are distributors setting RUSTFLAGS in .bashrc or something? Is this something distros are actually doing or just a theoretical possibility? 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 is commonly done in CFLAGS. I thought it was also done for RUSTFLAGS, but apparently not. 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. Arch ships RUSTFLAGS overrides in default configuration for makepkg. |
||
| believes they are turning on enforcing stack protection by using `-C stack-protector=strong`, | ||
| the application will not be getting enforcement due to the distribution setting | ||
| `-C allow-partial-mitigations=stack-protector`. | ||
|
|
||
| On the other hand, maybe there is not actually desire to add | ||
| `-C stack-protector=strong -C allow-partial-mitigations=stack-protector` as a default? | ||
|
|
||
| Maybe it is actually possible to ship a `-C stack-protector=strong` standard library and | ||
| add a `-C stack-protector=strong` default, since the enforcement check only works | ||
| "towards roots"? | ||
|
|
||
| #### With order dependency | ||
|
|
||
| With a small amount of implementation effort, we could have `-C stack-protector=strong` reset the | ||
| `-C allow-partial-mitigations=stack-protector` state, so that | ||
| `-C stack-protector=strong -C allow-partial-mitigations=stack-protector -C stack-protector=strong` | ||
| is equivalent to `-C stack-protector=strong`. | ||
|
|
||
| This would work quite well, but I am not sure that rustc wants to have order between different | ||
| kinds of CLI arguments. | ||
|
|
||
| ## Interaction with `-C unsafe-allow-abi-mismatch` / `-C pretend-mitigation-enabled` | ||
|
|
||
| The proposed rules do not interact with `-C unsafe-allow-abi-mismatch` at all, so if | ||
| you have a "sanitizer runtime" crate that is compiled with the following options: | ||
|
|
||
| > -C no-fixed-x18 -C sanitizer=shadow-call-stack=off -C unsafe-allow-abi-mismatch=fixed-x18 -C unsafe-allow-abi-mismatch=shadow-call-stack | ||
|
|
||
| Then dependencies will need to use it via `-C sanitizer=shadow-call-stack-noenforce` | ||
| rather than `-C sanitizer=shadow-call-stack`, otherwise they will get an error. | ||
|
|
||
| As far as I can need, there is no current demand for that sort of sanitizer runtime, | ||
| but if that is desired, it might be a good idea to add a | ||
| `-C pretend-mitigation-enabled=shadow-call-stack`, and possibly to make | ||
| `-C unsafe-allow-abi-mismatch` do that for crates that are target modifiers. | ||
|
|
||
| ## Defaults | ||
|
|
||
| We want that the most obvious way to enable mitigations (e.g. | ||
| `-C stack-protector=strong` or `-C sanitizer=shadow-call-stack`) to turn on | ||
| enforcement, since that will set people up to a pit of success where mitigations | ||
| are enabled throughout. | ||
|
|
||
| However, we do want an easy way for distribution owners (for example, | ||
| Ubuntu) to turn on mitigations in a non-enforcing way, as is done | ||
| today e.g. [by Ubuntu with `-fstack-protector-strong`]. Distributions can't easily | ||
| add a new mitigation in an enforcing way, as that will cause widespread | ||
| breakage, but they can fairly easily turn a mitigation on in a non-enforcing way. | ||
|
|
||
| We do want the combination of defaults to combine in a nice way - if the | ||
| distributioon sets `-C stack-protector=strong-noenforce`, and the user adds | ||
| `-C stack-protector=strong`, we want the result to be stack-protector set | ||
| to strong and enforcing. | ||
|
|
||
| On the other hand, maybe there is not actually desire to add | ||
| `-C stack-protector=strong -C allow-partial-mitigations=stack-protector` as a default, | ||
| which would make this less interesting? | ||
|
|
||
| Maybe it is actually possible to ship a `-C stack-protector=strong` standard library and | ||
| add a `-C stack-protector=strong` default, since the enforcement check only works | ||
| "towards roots"? | ||
arielb1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| [by Ubuntu with `-fstack-protector-strong`]: https://wiki.ubuntu.com/ToolChain/CompilerFlags | ||
|
|
||
| ## The standard library | ||
|
|
||
| One big place where it's very easy to end up with mixed mitigations is the | ||
| standard library. The standard library comes compiled with just a single | ||
| set of mitigations enabled (as of Rust 1.88: none), and without `-Z build-std`, | ||
wesleywiser marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| it is only possible to use the mitigation settings in the shipped standard | ||
| library. | ||
|
|
||
| If we find out that some mitigations have a positive cost-benefit ratio | ||
| for the standard library (probably at least [`-Z stack-protector`]), we | ||
| probably want to ship a standard library supporting them by default, but | ||
| in a way that still allows people to compile code without mitigations, | ||
| if that fulfills their cost/benefit ratios better. | ||
wesleywiser marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ## Why not target modifiers? | ||
|
|
||
| The [target modifier] feature provides a similar goal of preventing mismatches in compiler | ||
| settings. | ||
|
|
||
| There are several issues with using target modifiers for mitigations: | ||
|
|
||
| ### The name unsafe-allow-abi-mismatch | ||
|
|
||
| The name of the flag that allows mixing target modifiers, `-C unsafe-allow-abi-mismatch`, | ||
| does not make sense for cases that are not "unsafe ABI mismatches". It also uses the | ||
| word "unsafe", which we prefer not to use except in cases that can result in actual | ||
| unsoundness. | ||
|
|
||
| ### The behavior of unsafe-allow-abi-mismatch | ||
|
|
||
| The behavior of `-C unsafe-allow-abi-mismatch` is also not ideal for mitigations. | ||
|
|
||
| The flag marks a crate as basically having a "wildcard target modifier", which allows it | ||
| to compile with crates with any value of the target modifier. | ||
|
|
||
| This is quite good for the original use case - it's an "I know what I am doing" flag | ||
| that allows "runtime" crates to be mixed in even if they subtly play with the | ||
| ABI rules - for example, in a kernel, where floating point execution is mostly | ||
| forbidden, there are a few compilation units using real floats. To call into them, first | ||
| you call some special functions that make the floating point registers usable, and then | ||
| you call into the CU safely by not having any floats in the signature of the function on | ||
| the boundary ([example by Alice Ryhl]). | ||
|
|
||
| However, for mitigations, the expected case for disabling mitigations is less people | ||
| knowing what they are doing, and more people that don't agree with the performance/security | ||
| tradeoff they bring. In that case, we should allow the executable-writer to be aware | ||
| of the tradeoff being made, rather than letting libraries in the middle decide it | ||
| for them. | ||
|
|
||
| ## Why not an external tool? | ||
|
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. As a completely different alternative, it seems like we could extend rustc's CLI to support extracting information about the crate graph loaded during the compilation session into a specific format that can be read by other tools. For example, a JSON file which notes the name, location and compilation flags used to compile every crate loaded by the resolver during compilation. The mitigation enforcement mechanism could then be built on top of that data in addition to other use cases the RFC mentions (but does not satisfy) such as such as confirming all crates share the same Since we've already implemented one set of flags/policy to ensure crates agree on certain compiler flags (target modifiers) and now we're potentially implementing a second set with a slightly different policy, I think it would be helpful to explain why this approach should be preferred over a more general mechanism. 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 that target-modifier makes sense as a "hardening check", since it is required for soundness. For an ELF property: I don't think it makes sense for Rust to invent one. However: Fedora did invent a 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'm not suggesting that we remove target-modifiers. My comment is more that there are many reasons why it might be important for every crate in a given build graph to have been compiled with the same (or perhaps merely compatible) compiler flags. Target modifiers is a very important case as you point out because of soundness implications. Codegen security mitigations are another case where soundness is not at stake but the organization deploying the binary may have rules about what mitigations must have been used in a production environment. Likewise, some organizations also have requirements such as "debuginfo must have been generated" or "the target cpu must be This RFC proposes a solution to ensure some of those requirements are met appropriately. However, a different approach like the one I outlined above could be used to solve any arbitrary set of requirements by allowing users to build the tools and policy enforcement they need. I think it would be useful to at least consider that possibility and explain why we should not take that approach. (I personally think it is a compelling approach with less complexity than the current proposal while giving users more insight into their binaries. The downside of course is that it leaves enforcement entirely up to the user.) 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. K, for this my answer is that a process that involves creating some sort of JSON separate from the output binary and then analyzing that has a tendency of complicating the build process, and while it might be appropriate for very large projects such as the Linux kernel, it's probably inappropriate for the average project. On the other hand, we do have Cargo, and I think Cargo already handles multiple outputs? @rcvalle would you be OK with this being Actually, I don't think there is much contradiction between doing both. I don't think that mitigation enforcement logic belongs in Cargo. Let's ask the Cargo people. [For target modifiers, they definitely should be handled in rustc due to the Rust soundness guarantee belonging in rustc]. 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 my other concern is that a stable component graph format will take infinite bikeshedding. Are we fine with having a component graph format that is only stable for Cargo consumption? I definitely see the option where we have 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.
Since people already favor this in rustc rather than Cargo, I think my voice is not that important. Checking flags passed to rustc is technically possible to do in Cargo. Reading emitted compotent-info.json after build is also possible. It is hard to be done by third party tool in Cargo, as artifacts of transitive dependencies are kind private and plugin don't know where they are. Anyway, I agree with folks this is more useful in rustc. 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.
Actually this is currently not possible as Cargo doesn't parse rustflags |
||
|
|
||
| This is somewhat hard to do with an external tool, since there is | ||
| no way of looking at a binary and telling what mitigations its components | ||
| have (for example [`hardening-check(1)`], exists, but its check for | ||
| stack smashing protection only checks that at least 1 function has stack | ||
| cookies, rather than checking that every interesting function has it | ||
| enabled). | ||
|
|
||
| [`hardening-check(1)`]: https://manpages.debian.org/testing/devscripts/hardening-check.1.en.html | ||
|
|
||
| ## .note.gnu.property | ||
|
|
||
| The `.note.gnu.property` field contains a number of properties | ||
| (for example, [`GNU_PROPERTY_AARCH64_FEATURE_1_BTI`]) that are used to indicate | ||
| that the compiled code contains certain mitigations, for example BTI | ||
| (`-Zbranch-protection=bti`). | ||
|
|
||
| When linking multiple objects, the linker sets the resulting property to be the | ||
| logical AND of the properties of the constituent objects. | ||
|
|
||
| For protections such as BTI, the mitigation can only be turned on if all code | ||
| within the compiled binary supports it - if one of the object files doesn't, | ||
| the loader has to leave the mitigation turned off entirely. The ELF loader uses | ||
| the value of the property within the loaded executable to decide whether | ||
| to turn on the mitigation. | ||
|
|
||
| If it could be arranged, using `.note.gnu.property` could allow mitigation tracking | ||
| to propagate across languages - with the final compilation step intentionally erroring | ||
| out if the property is not enabled. However, this is also a disadvantage - adding a | ||
| new property to `.note.gnu.property` requires cooperation from the target owners. | ||
|
|
||
| Therefore, it might be useful as a future step with cooperation from the target owners, | ||
| but is not good if we want to be able to add new enforced mitigations without requiring | ||
| cooperation from all platforms. | ||
|
|
||
| [`GNU_PROPERTY_AARCH64_FEATURE_1_BTI`]: https://docs.rs/object/0.37/object/elf/constant.GNU_PROPERTY_AARCH64_FEATURE_1_BTI.html | ||
|
|
||
| # Prior art | ||
| [prior-art]: #prior-art | ||
|
|
||
| ## The panic strategy | ||
|
|
||
| The Rust compiler already *has* infrastructure to detect flag mismatches: the | ||
| flags `-Cpanic` and `-Zpanic-in-drop`. The prebuilt stdlib comes with different | ||
| pieces depending on which strategy is used, although panic landing flags are | ||
| not entirely removed when using `-Cpanic=abort`, as only part of the prebuilt | ||
| stdlib is switched out. | ||
|
|
||
| ## Target modifiers | ||
|
|
||
| ## .note.gnu.property | ||
|
|
||
| The `.note.gnu.property` section discussed previously is an example of C code | ||
| detecting mismatches of a flag at link time. | ||
|
|
||
| # Unresolved questions | ||
| [unresolved-questions]: #unresolved-questions | ||
|
|
||
| # Future possibilities | ||
| [future-possibilities]: #future-possibilities | ||
|
|
||
| A possible future extension could be to provide a mechanism to enforce | ||
| mitigations across C code and Rust code. This would be an interesting | ||
| extension, but it would require cross-language effort that will | ||
| take a long period of time to finish. Similarly, another possible | ||
| future extension could be to catch mitigation mismatches when using | ||
| dynamic linking. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's be cool if this was somehow per dependency, like maybe you'd add some
disable-mitigation-enforcement = {std}into the binary's Cargo.toml.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want something in the style of
-C allow-partial-mitigations=stack-protector=std+alloc+core, so you know which mitigations you are allowing.(Of course, with also a syntax in Cargo, which should come with a separate RFC I think).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added that to alternatives
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifying crate names from the sysroot is very problematic since std has about 10 different dependencies that would need to be specified and that are not stable.
What would we desired for this use case of allowing the sysroot and nothing else would be special syntax to allow it only in the sysroot. A list of crate names would not help.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think that special casing
core(or@coreor something) to apply to the entire sysroot would work?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would still be useful for crates outside std, for example, if a project experiences a mitigation "failure" in a non-security critical crate or feature branch.