From c0744e1e0c35b1083733fd5c74fc3fb5a6cd04f7 Mon Sep 17 00:00:00 2001 From: Andrew Paverd Date: Mon, 13 Jan 2020 13:25:39 +0000 Subject: [PATCH] Add support for Control Flow Guard on Windows. This patch enables rustc to emit the required LLVM module flags to enable Control Flow Guard metadata (cfguard=1) or metadata and checks (cfguard=2). The LLVM module flags are ignored on unsupported targets and operating systems. --- src/librustc_codegen_llvm/context.rs | 12 +++++++++++- src/librustc_codegen_ssa/back/link.rs | 6 +++++- src/librustc_codegen_ssa/back/linker.rs | 21 +++++++++++++++++++++ src/librustc_session/config.rs | 18 ++++++++++++++++-- src/librustc_session/options.rs | 16 +++++++++++++++- src/test/codegen/cfguard_checks.rs | 10 ++++++++++ src/test/codegen/cfguard_disabled.rs | 10 ++++++++++ src/test/codegen/cfguard_nochecks.rs | 10 ++++++++++ 8 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 src/test/codegen/cfguard_checks.rs create mode 100644 src/test/codegen/cfguard_disabled.rs create mode 100644 src/test/codegen/cfguard_nochecks.rs diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 50a35fe3dcf..6b31f14410d 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -12,7 +12,7 @@ use rustc_codegen_ssa::traits::*; use crate::callee::get_fn; use rustc::bug; use rustc::mir::mono::CodegenUnit; -use rustc::session::config::{self, DebugInfo}; +use rustc::session::config::{self, CFGuard, DebugInfo}; use rustc::session::Session; use rustc::ty::layout::{ FnAbiExt, HasParamEnv, LayoutError, LayoutOf, PointeeInfo, Size, TyLayout, VariantIdx, @@ -227,6 +227,16 @@ pub unsafe fn create_module( llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1); } + // Set module flags to enable Windows Control Flow Guard (/guard:cf) metadata + // only (`cfguard=1`) or metadata and checks (`cfguard=2`). + match sess.opts.debugging_opts.control_flow_guard { + CFGuard::Disabled => {} + CFGuard::NoChecks => { + llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 1) + } + CFGuard::Checks => llvm::LLVMRustAddModuleFlag(llmod, "cfguard\0".as_ptr() as *const _, 2), + } + llmod } diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index f56a4170c0a..bc0322ffe1d 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -1,7 +1,7 @@ use rustc::middle::cstore::{EncodedMetadata, LibSource, NativeLibrary, NativeLibraryKind}; use rustc::middle::dependency_format::Linkage; use rustc::session::config::{ - self, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer, + self, CFGuard, DebugInfo, OutputFilenames, OutputType, PrintRequest, Sanitizer, }; use rustc::session::search_paths::PathKind; /// For all the linkers we support, and information they might @@ -1294,6 +1294,10 @@ fn link_args<'a, B: ArchiveBuilder<'a>>( cmd.pgo_gen(); } + if sess.opts.debugging_opts.control_flow_guard != CFGuard::Disabled { + cmd.control_flow_guard(); + } + // FIXME (#2397): At some point we want to rpath our guesses as to // where extern libraries might live, based on the // addl_lib_search_paths diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs index 695f171dfb4..5aafb8a12d7 100644 --- a/src/librustc_codegen_ssa/back/linker.rs +++ b/src/librustc_codegen_ssa/back/linker.rs @@ -106,6 +106,7 @@ pub trait Linker { fn no_relro(&mut self); fn optimize(&mut self); fn pgo_gen(&mut self); + fn control_flow_guard(&mut self); fn debuginfo(&mut self); fn no_default_libraries(&mut self); fn build_dylib(&mut self, out_filename: &Path); @@ -360,6 +361,10 @@ impl<'a> Linker for GccLinker<'a> { self.cmd.arg("__llvm_profile_runtime"); } + fn control_flow_guard(&mut self) { + self.sess.warn("Windows Control Flow Guard is not supported by this linker."); + } + fn debuginfo(&mut self) { if let DebugInfo::None = self.sess.opts.debuginfo { // If we are building without debuginfo enabled and we were called with @@ -660,6 +665,10 @@ impl<'a> Linker for MsvcLinker<'a> { // Nothing needed here. } + fn control_flow_guard(&mut self) { + self.cmd.arg("/guard:cf"); + } + fn debuginfo(&mut self) { // This will cause the Microsoft linker to generate a PDB file // from the CodeView line tables in the object files. @@ -862,6 +871,10 @@ impl<'a> Linker for EmLinker<'a> { // noop, but maybe we need something like the gnu linker? } + fn control_flow_guard(&mut self) { + self.sess.warn("Windows Control Flow Guard is not supported by this linker."); + } + fn debuginfo(&mut self) { // Preserve names or generate source maps depending on debug info self.cmd.arg(match self.sess.opts.debuginfo { @@ -1058,6 +1071,10 @@ impl<'a> Linker for WasmLd<'a> { fn debuginfo(&mut self) {} + fn control_flow_guard(&mut self) { + self.sess.warn("Windows Control Flow Guard is not supported by this linker."); + } + fn no_default_libraries(&mut self) {} fn build_dylib(&mut self, _out_filename: &Path) { @@ -1233,6 +1250,10 @@ impl<'a> Linker for PtxLinker<'a> { fn no_default_libraries(&mut self) {} + fn control_flow_guard(&mut self) { + self.sess.warn("Windows Control Flow Guard is not supported by this linker."); + } + fn build_dylib(&mut self, _out_filename: &Path) {} fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) {} diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs index aa492b566e5..813d14d616d 100644 --- a/src/librustc_session/config.rs +++ b/src/librustc_session/config.rs @@ -70,6 +70,19 @@ impl FromStr for Sanitizer { } } +/// The different settings that the `-Z control_flow_guard` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CFGuard { + /// Do not emit Control Flow Guard metadata or checks. + Disabled, + + /// Emit Control Flow Guard metadata but no checks. + NoChecks, + + /// Emit Control Flow Guard metadata and checks. + Checks, +} + #[derive(Clone, Copy, Debug, PartialEq, Hash)] pub enum OptLevel { No, // -O0 @@ -1980,8 +1993,8 @@ impl PpMode { /// how the hash should be calculated when adding a new command-line argument. crate mod dep_tracking { use super::{ - CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, OutputTypes, - Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion, + CFGuard, CrateType, DebugInfo, ErrorOutputType, LinkerPluginLto, LtoCli, OptLevel, + OutputTypes, Passes, Sanitizer, SwitchWithOptPath, SymbolManglingVersion, }; use crate::lint; use crate::utils::NativeLibraryKind; @@ -2053,6 +2066,7 @@ crate mod dep_tracking { impl_dep_tracking_hash_via_hash!(NativeLibraryKind); impl_dep_tracking_hash_via_hash!(Sanitizer); impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(CFGuard); impl_dep_tracking_hash_via_hash!(TargetTriple); impl_dep_tracking_hash_via_hash!(Edition); impl_dep_tracking_hash_via_hash!(LinkerPluginLto); diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index 34da2188a51..f7f8ec86045 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -263,6 +263,8 @@ macro_rules! options { pub const parse_sanitizer_list: Option<&str> = Some("comma separated list of sanitizers"); pub const parse_sanitizer_memory_track_origins: Option<&str> = None; + pub const parse_cfguard: Option<&str> = + Some("either `disabled`, `nochecks`, or `checks`"); pub const parse_linker_flavor: Option<&str> = Some(::rustc_target::spec::LinkerFlavor::one_of()); pub const parse_optimization_fuel: Option<&str> = @@ -288,7 +290,7 @@ macro_rules! options { #[allow(dead_code)] mod $mod_set { use super::{$struct_name, Passes, Sanitizer, LtoCli, LinkerPluginLto, SwitchWithOptPath, - SymbolManglingVersion}; + SymbolManglingVersion, CFGuard}; use rustc_target::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, RelroLevel}; use std::path::PathBuf; use std::str::FromStr; @@ -499,6 +501,16 @@ macro_rules! options { } } + fn parse_cfguard(slot: &mut CFGuard, v: Option<&str>) -> bool { + match v { + Some("disabled") => *slot = CFGuard::Disabled, + Some("nochecks") => *slot = CFGuard::NoChecks, + Some("checks") => *slot = CFGuard::Checks, + _ => return false, + } + true + } + fn parse_linker_flavor(slote: &mut Option, v: Option<&str>) -> bool { match v.and_then(LinkerFlavor::from_str) { Some(lf) => *slote = Some(lf), @@ -950,6 +962,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, (such as entering an empty infinite loop) by inserting llvm.sideeffect"), deduplicate_diagnostics: Option = (None, parse_opt_bool, [UNTRACKED], "deduplicate identical diagnostics"), + control_flow_guard: CFGuard = (CFGuard::Disabled, parse_cfguard, [UNTRACKED], + "use Windows Control Flow Guard (`disabled`, `nochecks` or `checks`)"), no_link: bool = (false, parse_bool, [TRACKED], "compile without linking"), } diff --git a/src/test/codegen/cfguard_checks.rs b/src/test/codegen/cfguard_checks.rs new file mode 100644 index 00000000000..40a7353eac0 --- /dev/null +++ b/src/test/codegen/cfguard_checks.rs @@ -0,0 +1,10 @@ +// compile-flags: -Z control_flow_guard=checks + +#![crate_type = "lib"] + +// A basic test function. +pub fn test() { +} + +// Ensure the module flag cfguard=2 is present +// CHECK: !"cfguard", i32 2 diff --git a/src/test/codegen/cfguard_disabled.rs b/src/test/codegen/cfguard_disabled.rs new file mode 100644 index 00000000000..d1747931e15 --- /dev/null +++ b/src/test/codegen/cfguard_disabled.rs @@ -0,0 +1,10 @@ +// compile-flags: -Z control_flow_guard=disabled + +#![crate_type = "lib"] + +// A basic test function. +pub fn test() { +} + +// Ensure the module flag cfguard is not present +// CHECK-NOT: !"cfguard" diff --git a/src/test/codegen/cfguard_nochecks.rs b/src/test/codegen/cfguard_nochecks.rs new file mode 100644 index 00000000000..c5d7afbae25 --- /dev/null +++ b/src/test/codegen/cfguard_nochecks.rs @@ -0,0 +1,10 @@ +// compile-flags: -Z control_flow_guard=nochecks + +#![crate_type = "lib"] + +// A basic test function. +pub fn test() { +} + +// Ensure the module flag cfguard=1 is present +// CHECK: !"cfguard", i32 1