-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Add support for control-flow protection #93439
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
Changes from all commits
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 |
---|---|---|
|
@@ -380,6 +380,7 @@ mod desc { | |
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2"; | ||
pub const parse_cfguard: &str = | ||
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`"; | ||
pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)"; | ||
pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`"; | ||
pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of(); | ||
pub const parse_optimization_fuel: &str = "crate=integer"; | ||
|
@@ -695,6 +696,25 @@ mod parse { | |
true | ||
} | ||
|
||
crate fn parse_cfprotection(slot: &mut CFProtection, v: Option<&str>) -> bool { | ||
if v.is_some() { | ||
let mut bool_arg = None; | ||
if parse_opt_bool(&mut bool_arg, v) { | ||
abrown marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*slot = if bool_arg.unwrap() { CFProtection::Full } else { CFProtection::None }; | ||
return true; | ||
} | ||
} | ||
|
||
*slot = match v { | ||
None | Some("none") => CFProtection::None, | ||
Some("branch") => CFProtection::Branch, | ||
Some("return") => CFProtection::Return, | ||
Some("full") => CFProtection::Full, | ||
Some(_) => return false, | ||
}; | ||
Comment on lines
+708
to
+714
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 seems like it'd be pretty hard to extend this kind of CLI in a sensible manner in the future if the underlying CF protection concept changes in any way. 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. To address this and your comments below about wanting a unified |
||
true | ||
} | ||
|
||
crate fn parse_linker_flavor(slot: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool { | ||
match v.and_then(LinkerFlavor::from_str) { | ||
Some(lf) => *slot = Some(lf), | ||
|
@@ -1142,6 +1162,8 @@ options! { | |
"select which borrowck is used (`mir` or `migrate`) (default: `migrate`)"), | ||
branch_protection: BranchProtection = (BranchProtection::default(), parse_branch_protection, [TRACKED], | ||
"set options for branch target identification and pointer authentication on AArch64"), | ||
cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED], | ||
"instrument control-flow architecture protection"), | ||
cgu_partitioning_strategy: Option<String> = (None, parse_opt_string, [TRACKED], | ||
"the codegen unit partitioning strategy to use"), | ||
chalk: bool = (false, parse_bool, [TRACKED], | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# `cf-protection` | ||
|
||
This option enables control-flow enforcement technology (CET) on x86; a more detailed description of | ||
CET is available [here]. Similar to `clang`, this flag takes one of the following values: | ||
|
||
- `none` - Disable CET completely (this is the default). | ||
- `branch` - Enable indirect branch tracking (`IBT`). | ||
- `return` - Enable shadow stack (`SHSTK`). | ||
- `full` - Enable both `branch` and `return`. | ||
|
||
[here]: https://www.intel.com/content/www/us/en/develop/articles/technical-look-control-flow-enforcement-technology.html | ||
|
||
This flag only applies to the LLVM backend: it sets the `cf-protection-branch` and | ||
`cf-protection-return` flags on LLVM modules. Note, however, that all compiled modules linked | ||
together must have the flags set for the compiled output to be CET-enabled. Currently, Rust's | ||
standard library does not ship with CET enabled by default, so you may need to rebuild all standard | ||
modules with a `cargo` command like: | ||
|
||
```sh | ||
$ RUSTFLAGS="-Z cf-protection=full" RUSTC="rustc-custom" cargo +nightly build -Z build-std --target x86_64-unknown-linux-gnu | ||
``` | ||
|
||
### Detection | ||
|
||
An ELF binary is CET-enabled if it has the `IBT` and `SHSTK` tags, e.g.: | ||
|
||
```sh | ||
$ readelf -a target/x86_64-unknown-linux-gnu/debug/example | grep feature: | ||
Properties: x86 feature: IBT, SHSTK | ||
``` | ||
|
||
### Troubleshooting | ||
|
||
To display modules that are not CET enabled, examine the linker errors available when `cet-report` is enabled: | ||
|
||
```sh | ||
$ RUSTC_LOG=rustc_codegen_ssa::back::link=info rustc-custom -v -Z cf-protection=full -C link-arg="-Wl,-z,cet-report=warning" -o example example.rs | ||
... | ||
/usr/bin/ld: /.../build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-d73f7266be14cb8b.rlib(std-d73f7266be14cb8b.std.f7443020-cgu.12.rcgu.o): warning: missing IBT and SHSTK properties | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Test that the correct module flags are emitted with different control-flow protection flags. | ||
|
||
// revisions: undefined none branch return full | ||
// needs-llvm-components: x86 | ||
// [undefined] compile-flags: | ||
// [none] compile-flags: -Z cf-protection=none | ||
// [branch] compile-flags: -Z cf-protection=branch | ||
// [return] compile-flags: -Z cf-protection=return | ||
// [full] compile-flags: -Z cf-protection=full | ||
// compile-flags: --target x86_64-unknown-linux-gnu | ||
|
||
#![crate_type = "lib"] | ||
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 needs to be 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. Ok, re-submitted. I'm still not exactly sure why this must be 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. That's because the full rustc test suite involves running on architectures other than
and yet the standard library is not made available for this target, leading to a test failure. Hence
instead, but that would mean anybody developing the compiler on non-x86 machines would not get the coverage this test provides. 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. Also, what is the correct state to put this PR in? Should I give another rustbot command to change the label? Or do you? |
||
#![feature(no_core, lang_items)] | ||
#![no_core] | ||
|
||
#[lang="sized"] | ||
trait Sized { } | ||
|
||
// A basic test function. | ||
pub fn test() { | ||
} | ||
|
||
// undefined-NOT: !"cf-protection-branch" | ||
// undefined-NOT: !"cf-protection-return" | ||
|
||
// none-NOT: !"cf-protection-branch" | ||
// none-NOT: !"cf-protection-return" | ||
|
||
// branch-NOT: !"cf-protection-return" | ||
// branch: !"cf-protection-branch", i32 1 | ||
// branch-NOT: !"cf-protection-return" | ||
|
||
// return-NOT: !"cf-protection-branch" | ||
// return: !"cf-protection-return", i32 1 | ||
// return-NOT: !"cf-protection-branch" | ||
|
||
// full: !"cf-protection-branch", i32 1 | ||
// full: !"cf-protection-return", i32 1 |
Uh oh!
There was an error while loading. Please reload this page.