Skip to content
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

Enable Rust to use the EHCont security feature of Windows #118013

Merged
merged 4 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,16 @@ pub unsafe fn create_module<'ll>(
);
}

// Set module flag to enable Windows EHCont Guard (/guard:ehcont).
if sess.opts.unstable_opts.ehcont_guard {
llvm::LLVMRustAddModuleFlag(
llmod,
llvm::LLVMModFlagBehavior::Warning,
"ehcontguard\0".as_ptr() as *const _,
1,
)
}

// Insert `llvm.ident` metadata.
//
// On the wasm targets it will get hooked up to the "producer" sections
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2378,6 +2378,11 @@ fn add_order_independent_options(
cmd.control_flow_guard();
}

// OBJECT-FILES-NO, AUDIT-ORDER
if sess.opts.unstable_opts.ehcont_guard {
cmd.ehcont_guard();
}

add_rpath_args(cmd, sess, codegen_results, out_filename);
}

Expand Down
21 changes: 21 additions & 0 deletions compiler/rustc_codegen_ssa/src/back/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {}
Expand Down Expand Up @@ -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) {}
}

Expand Down Expand Up @@ -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 => {}
Expand Down Expand Up @@ -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) {}
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1582,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],
Expand Down
4 changes: 4 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions src/bootstrap/src/core/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1964,6 +1964,16 @@ impl<'a> Builder<'a> {
rustflags.arg("-Ccontrol-flow-guard");
}

// 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("-Zehcont-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
Expand Down
3 changes: 3 additions & 0 deletions src/bootstrap/src/core/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf>,
Expand Down Expand Up @@ -1019,6 +1020,7 @@ define_config! {
test_compare_mode: Option<bool> = "test-compare-mode",
llvm_libunwind: Option<String> = "llvm-libunwind",
control_flow_guard: Option<bool> = "control-flow-guard",
ehcont_guard: Option<bool> = "ehcont-guard",
new_symbol_mangling: Option<bool> = "new-symbol-mangling",
profile_generate: Option<String> = "profile-generate",
profile_use: Option<String> = "profile-use",
Expand Down Expand Up @@ -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"));
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/src/tests/builder.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
10 changes: 10 additions & 0 deletions tests/codegen/ehcontguard_disabled.rs
Original file line number Diff line number Diff line change
@@ -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"
10 changes: 10 additions & 0 deletions tests/codegen/ehcontguard_enabled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: -Z ehcont-guard

#![crate_type = "lib"]

// A basic test function.
pub fn test() {
}

// Ensure the module flag ehcontguard=1 is present
// CHECK: !"ehcontguard", i32 1