From e11d8d147b94043d54d0a6c8ff0bdb0568b794ca Mon Sep 17 00:00:00 2001 From: Arlie Davis Date: Fri, 17 Nov 2023 10:05:38 -0800 Subject: [PATCH 1/4] Add support for generating the EHCont section In the future Windows will enable Control-flow Enforcement Technology (CET aka Shadow Stacks). To protect the path where the context is updated during exception handling, the binary is required to enumerate valid unwind entrypoints in a dedicated section which is validated when the context is being set during exception handling. The required support for EHCONT has already been merged into LLVM, long ago. This change adds the Rust codegen option to enable it. Reference: * https://reviews.llvm.org/D40223 This also adds a new `ehcont-guard` option to the bootstrap config which enables EHCont Guard when building std. --- compiler/rustc_codegen_llvm/src/context.rs | 9 ++++++++ compiler/rustc_codegen_ssa/src/back/link.rs | 5 +++++ compiler/rustc_codegen_ssa/src/back/linker.rs | 21 +++++++++++++++++++ compiler/rustc_session/src/options.rs | 2 ++ config.example.toml | 4 ++++ src/bootstrap/src/core/builder.rs | 12 ++++++++++- src/bootstrap/src/core/config/config.rs | 3 +++ src/bootstrap/src/tests/builder.rs | 2 +- tests/codegen/ehcontguard_disabled.rs | 10 +++++++++ tests/codegen/ehcontguard_enabled.rs | 10 +++++++++ 10 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 tests/codegen/ehcontguard_disabled.rs create mode 100644 tests/codegen/ehcontguard_enabled.rs diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 242c6aed906b4..883a4b5f6fb0e 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -350,6 +350,15 @@ pub unsafe fn create_module<'ll>( 1, ); } + // Set module flag to enable Windows EHCont Guard (/guard:ehcont). + if sess.opts.cg.ehcont_guard { + llvm::LLVMRustAddModuleFlag( + llmod, + llvm::LLVMModFlagBehavior::Warning, + "ehcontguard\0".as_ptr() as *const _, + 1, + ) + } // Insert `llvm.ident` metadata. // diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 33e8f352cd8ab..e571912973cc0 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2378,6 +2378,11 @@ fn add_order_independent_options( cmd.control_flow_guard(); } + // OBJECT-FILES-NO, AUDIT-ORDER + if sess.opts.cg.ehcont_guard { + cmd.ehcont_guard(); + } + add_rpath_args(cmd, sess, codegen_results, out_filename); } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 09434513e31e9..73c4aa9712d0c 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -185,6 +185,7 @@ pub trait Linker { fn optimize(&mut self); fn pgo_gen(&mut self); fn control_flow_guard(&mut self); + fn ehcont_guard(&mut self); fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]); fn no_crt_objects(&mut self); fn no_default_libraries(&mut self); @@ -605,6 +606,8 @@ impl<'a> Linker for GccLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { // MacOS linker doesn't support stripping symbols directly anymore. if self.sess.target.is_like_osx { @@ -914,6 +917,12 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg("/guard:cf"); } + fn ehcont_guard(&mut self) { + if self.sess.target.pointer_width == 64 { + self.cmd.arg("/guard:ehcont"); + } + } + fn debuginfo(&mut self, strip: Strip, natvis_debugger_visualizers: &[PathBuf]) { match strip { Strip::None => { @@ -1127,6 +1136,8 @@ impl<'a> Linker for EmLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) { // Preserve names or generate source maps depending on debug info // For more information see https://emscripten.org/docs/tools_reference/emcc.html#emcc-g @@ -1319,6 +1330,8 @@ impl<'a> Linker for WasmLd<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn no_crt_objects(&mut self) {} fn no_default_libraries(&mut self) {} @@ -1472,6 +1485,8 @@ impl<'a> Linker for L4Bender<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn no_crt_objects(&mut self) {} } @@ -1613,6 +1628,8 @@ impl<'a> Linker for AixLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { match strip { Strip::None => {} @@ -1835,6 +1852,8 @@ impl<'a> Linker for PtxLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, _symbols: &[String]) {} fn subsystem(&mut self, _subsystem: &str) {} @@ -1931,6 +1950,8 @@ impl<'a> Linker for BpfLinker<'a> { fn control_flow_guard(&mut self) {} + fn ehcont_guard(&mut self) {} + fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("symbols"); let res: io::Result<()> = try { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b824eb51ef7be..924bd97b4d54d 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1387,6 +1387,8 @@ options! { "allow the linker to link its default libraries (default: no)"), dlltool: Option = (None, parse_opt_pathbuf, [UNTRACKED], "import library generation tool (ignored except when targeting windows-gnu)"), + ehcont_guard: bool = (false, parse_bool, [TRACKED], + "generate Windows EHCont Guard tables"), embed_bitcode: bool = (true, parse_bool, [TRACKED], "emit bitcode in rlibs (default: yes)"), extra_filename: String = (String::new(), parse_string, [UNTRACKED], diff --git a/config.example.toml b/config.example.toml index 170856bd97dbd..5f9ae039b252a 100644 --- a/config.example.toml +++ b/config.example.toml @@ -686,6 +686,10 @@ change-id = 116881 # This only applies from stage 1 onwards, and only for Windows targets. #control-flow-guard = false +# Enable Windows EHCont Guard checks in the standard library. +# This only applies from stage 1 onwards, and only for Windows targets. +#ehcont-guard = false + # Enable symbol-mangling-version v0. This can be helpful when profiling rustc, # as generics will be preserved in symbols (rather than erased into opaque T). # When no setting is given, the new scheme will be used when compiling the diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index c755324df1a57..de35e97848af1 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1964,6 +1964,12 @@ impl<'a> Builder<'a> { rustflags.arg("-Ccontrol-flow-guard"); } + // Same for EHCont Guard (this is not combined with the previous if-statement to make + // merges with upstream easier). + if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 { + rustflags.arg("-Cehcont-guard"); + } + // For `cargo doc` invocations, make rustdoc print the Rust version into the docs // This replaces spaces with tabs because RUSTDOCFLAGS does not // support arguments with regular spaces. Hopefully someday Cargo will @@ -2172,7 +2178,11 @@ impl<'a> Builder<'a> { } // Only execute if it's supposed to run as default - if desc.default && should_run.is_really_default() { self.ensure(step) } else { None } + if desc.default && should_run.is_really_default() { + self.ensure(step) + } else { + None + } } /// Checks if any of the "should_run" paths is in the `Builder` paths. diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index fa8b0b20cec45..9ef90798590ce 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -248,6 +248,7 @@ pub struct Config { pub local_rebuild: bool, pub jemalloc: bool, pub control_flow_guard: bool, + pub ehcont_guard: bool, // dist misc pub dist_sign_folder: Option, @@ -1019,6 +1020,7 @@ define_config! { test_compare_mode: Option = "test-compare-mode", llvm_libunwind: Option = "llvm-libunwind", control_flow_guard: Option = "control-flow-guard", + ehcont_guard: Option = "ehcont-guard", new_symbol_mangling: Option = "new-symbol-mangling", profile_generate: Option = "profile-generate", profile_use: Option = "profile-use", @@ -1452,6 +1454,7 @@ impl Config { config.rust_thin_lto_import_instr_limit = rust.thin_lto_import_instr_limit; set(&mut config.rust_remap_debuginfo, rust.remap_debuginfo); set(&mut config.control_flow_guard, rust.control_flow_guard); + set(&mut config.ehcont_guard, rust.ehcont_guard); config.llvm_libunwind_default = rust .llvm_libunwind .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")); diff --git a/src/bootstrap/src/tests/builder.rs b/src/bootstrap/src/tests/builder.rs index 96139f7b099ce..744015e8e8204 100644 --- a/src/bootstrap/src/tests/builder.rs +++ b/src/bootstrap/src/tests/builder.rs @@ -1,6 +1,6 @@ use super::*; -use crate::core::config::{Config, DryRun, TargetSelection}; use crate::core::build_steps::doc::DocumentationFormat; +use crate::core::config::{Config, DryRun, TargetSelection}; use std::thread; fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { diff --git a/tests/codegen/ehcontguard_disabled.rs b/tests/codegen/ehcontguard_disabled.rs new file mode 100644 index 0000000000000..7773384e5ead4 --- /dev/null +++ b/tests/codegen/ehcontguard_disabled.rs @@ -0,0 +1,10 @@ +// compile-flags: + +#![crate_type = "lib"] + +// A basic test function. +pub fn test() { +} + +// Ensure the module flag ehcontguard is not present +// CHECK-NOT: !"ehcontguard" diff --git a/tests/codegen/ehcontguard_enabled.rs b/tests/codegen/ehcontguard_enabled.rs new file mode 100644 index 0000000000000..5e7ea90940e74 --- /dev/null +++ b/tests/codegen/ehcontguard_enabled.rs @@ -0,0 +1,10 @@ +// compile-flags: -C ehcont_guard + +#![crate_type = "lib"] + +// A basic test function. +pub fn test() { +} + +// Ensure the module flag ehcontguard=1 is present +// CHECK: !"ehcontguard", i32 1 From d582f1092b524ed8aff86193928a1c7f871b11cf Mon Sep 17 00:00:00 2001 From: Arlie Davis Date: Fri, 17 Nov 2023 12:54:20 -0800 Subject: [PATCH 2/4] x.py fmt --- src/bootstrap/src/core/builder.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index de35e97848af1..5fd0d6a50ca6a 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -2178,11 +2178,7 @@ impl<'a> Builder<'a> { } // Only execute if it's supposed to run as default - if desc.default && should_run.is_really_default() { - self.ensure(step) - } else { - None - } + if desc.default && should_run.is_really_default() { self.ensure(step) } else { None } } /// Checks if any of the "should_run" paths is in the `Builder` paths. From 9429d68842a90fec21b5bed7419840bd86baaeab Mon Sep 17 00:00:00 2001 From: Arlie Davis Date: Tue, 21 Nov 2023 14:24:23 -0800 Subject: [PATCH 3/4] convert ehcont-guard to an unstable option --- compiler/rustc_codegen_llvm/src/context.rs | 3 ++- compiler/rustc_codegen_ssa/src/back/link.rs | 2 +- compiler/rustc_session/src/options.rs | 4 ++-- tests/codegen/ehcontguard_enabled.rs | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 883a4b5f6fb0e..e6c5085cc0efd 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -350,8 +350,9 @@ pub unsafe fn create_module<'ll>( 1, ); } + // Set module flag to enable Windows EHCont Guard (/guard:ehcont). - if sess.opts.cg.ehcont_guard { + if sess.opts.unstable_opts.ehcont_guard { llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Warning, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index e571912973cc0..b35ffac91b7e2 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2379,7 +2379,7 @@ fn add_order_independent_options( } // OBJECT-FILES-NO, AUDIT-ORDER - if sess.opts.cg.ehcont_guard { + if sess.opts.unstable_opts.ehcont_guard { cmd.ehcont_guard(); } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 924bd97b4d54d..4e669c81bf327 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1387,8 +1387,6 @@ options! { "allow the linker to link its default libraries (default: no)"), dlltool: Option = (None, parse_opt_pathbuf, [UNTRACKED], "import library generation tool (ignored except when targeting windows-gnu)"), - ehcont_guard: bool = (false, parse_bool, [TRACKED], - "generate Windows EHCont Guard tables"), embed_bitcode: bool = (true, parse_bool, [TRACKED], "emit bitcode in rlibs (default: yes)"), extra_filename: String = (String::new(), parse_string, [UNTRACKED], @@ -1584,6 +1582,8 @@ options! { "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), dylib_lto: bool = (false, parse_bool, [UNTRACKED], "enables LTO for dylib crate type"), + ehcont_guard: bool = (false, parse_bool, [TRACKED], + "generate Windows EHCont Guard tables"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), emit_thin_lto: bool = (true, parse_bool, [TRACKED], diff --git a/tests/codegen/ehcontguard_enabled.rs b/tests/codegen/ehcontguard_enabled.rs index 5e7ea90940e74..03aaa342b9678 100644 --- a/tests/codegen/ehcontguard_enabled.rs +++ b/tests/codegen/ehcontguard_enabled.rs @@ -1,4 +1,4 @@ -// compile-flags: -C ehcont_guard +// compile-flags: -Z ehcont-guard #![crate_type = "lib"] From 80896cbe351aa31ddc2f805c71e7eff85495d902 Mon Sep 17 00:00:00 2001 From: Arlie Davis Date: Tue, 21 Nov 2023 14:35:02 -0800 Subject: [PATCH 4/4] update -Cehcont-guard and comment --- src/bootstrap/src/core/builder.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 5fd0d6a50ca6a..507306fd274c8 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -1964,10 +1964,14 @@ impl<'a> Builder<'a> { rustflags.arg("-Ccontrol-flow-guard"); } - // Same for EHCont Guard (this is not combined with the previous if-statement to make - // merges with upstream easier). + // If EHCont Guard is enabled, pass the `-Zehcont-guard` flag to rustc when compiling the + // standard library, since this might be linked into the final outputs produced by rustc. + // Since this mitigation is only available on Windows, only enable it for the standard + // library in case the compiler is run on a non-Windows platform. + // This is not needed for stage 0 artifacts because these will only be used for building + // the stage 1 compiler. if cfg!(windows) && mode == Mode::Std && self.config.ehcont_guard && compiler.stage >= 1 { - rustflags.arg("-Cehcont-guard"); + rustflags.arg("-Zehcont-guard"); } // For `cargo doc` invocations, make rustdoc print the Rust version into the docs