From 230668765d5a1c435cfd6d6a101aaee0f67de2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20L=C3=B6thberg?= Date: Mon, 10 Jul 2017 20:57:45 +0200 Subject: [PATCH 1/5] Add support for full RELRO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds support for full RELRO, and enables it for the platforms I know have support for it. Full RELRO makes the PLT+GOT data read-only on startup, preventing it from being overwritten. http://tk-blog.blogspot.com/2009/02/relro-not-so-well-known-memory.html Fixes rust-lang/rust#29877. Signed-off-by: Johannes Löthberg --- src/librustc_back/target/bitrig_base.rs | 1 + src/librustc_back/target/dragonfly_base.rs | 1 + src/librustc_back/target/freebsd_base.rs | 1 + src/librustc_back/target/haiku_base.rs | 1 + src/librustc_back/target/linux_base.rs | 1 + src/librustc_back/target/mod.rs | 6 ++++++ src/librustc_back/target/netbsd_base.rs | 1 + src/librustc_back/target/openbsd_base.rs | 1 + src/librustc_trans/back/link.rs | 4 ++++ src/librustc_trans/back/linker.rs | 10 ++++++++++ 10 files changed, 27 insertions(+) diff --git a/src/librustc_back/target/bitrig_base.rs b/src/librustc_back/target/bitrig_base.rs index 5c4e01886a434..5a0ab83cd7231 100644 --- a/src/librustc_back/target/bitrig_base.rs +++ b/src/librustc_back/target/bitrig_base.rs @@ -19,6 +19,7 @@ pub fn opts() -> TargetOptions { linker_is_gnu: true, has_rpath: true, position_independent_executables: true, + full_relro: true, .. Default::default() } diff --git a/src/librustc_back/target/dragonfly_base.rs b/src/librustc_back/target/dragonfly_base.rs index e44cd393289be..ca116e82379b5 100644 --- a/src/librustc_back/target/dragonfly_base.rs +++ b/src/librustc_back/target/dragonfly_base.rs @@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, + full_relro: true, exe_allocation_crate: super::maybe_jemalloc(), .. Default::default() } diff --git a/src/librustc_back/target/freebsd_base.rs b/src/librustc_back/target/freebsd_base.rs index e44cd393289be..ca116e82379b5 100644 --- a/src/librustc_back/target/freebsd_base.rs +++ b/src/librustc_back/target/freebsd_base.rs @@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, + full_relro: true, exe_allocation_crate: super::maybe_jemalloc(), .. Default::default() } diff --git a/src/librustc_back/target/haiku_base.rs b/src/librustc_back/target/haiku_base.rs index 8e7f463563c38..c52b28708c36d 100644 --- a/src/librustc_back/target/haiku_base.rs +++ b/src/librustc_back/target/haiku_base.rs @@ -18,6 +18,7 @@ pub fn opts() -> TargetOptions { executables: true, has_rpath: false, target_family: Some("unix".to_string()), + full_relro: true, linker_is_gnu: true, no_integrated_as: true, .. Default::default() diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs index 722d2fa16ef7a..e4e7f062f88f6 100644 --- a/src/librustc_back/target/linux_base.rs +++ b/src/librustc_back/target/linux_base.rs @@ -36,6 +36,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, + full_relro: true, exe_allocation_crate: super::maybe_jemalloc(), has_elf_tls: true, .. Default::default() diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index edbbcf6f0b647..673c01f4b7e7d 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -367,6 +367,9 @@ pub struct TargetOptions { /// the functions in the executable are not randomized and can be used /// during an exploit of a vulnerability in any code. pub position_independent_executables: bool, + /// Full RELRO makes the dynamic linker resolve all symbols at startup and marks the GOT + /// read-only before starting the program, preventing overwriting the GOT. + pub full_relro: bool, /// Format that archives should be emitted in. This affects whether we use /// LLVM to assemble an archive or fall back to the system linker, and /// currently only "gnu" is used to fall into LLVM. Unknown strings cause @@ -454,6 +457,7 @@ impl Default for TargetOptions { has_rpath: false, no_default_libraries: true, position_independent_executables: false, + full_relro: false, pre_link_objects_exe: Vec::new(), pre_link_objects_dll: Vec::new(), post_link_objects: Vec::new(), @@ -683,6 +687,7 @@ impl Target { key!(has_rpath, bool); key!(no_default_libraries, bool); key!(position_independent_executables, bool); + key!(full_relro, bool); key!(archive_format); key!(allow_asm, bool); key!(custom_unwind_resume, bool); @@ -870,6 +875,7 @@ impl ToJson for Target { target_option_val!(has_rpath); target_option_val!(no_default_libraries); target_option_val!(position_independent_executables); + target_option_val!(full_relro); target_option_val!(archive_format); target_option_val!(allow_asm); target_option_val!(custom_unwind_resume); diff --git a/src/librustc_back/target/netbsd_base.rs b/src/librustc_back/target/netbsd_base.rs index 63245fcae767b..1d7d1b36008aa 100644 --- a/src/librustc_back/target/netbsd_base.rs +++ b/src/librustc_back/target/netbsd_base.rs @@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, + full_relro: true, .. Default::default() } } diff --git a/src/librustc_back/target/openbsd_base.rs b/src/librustc_back/target/openbsd_base.rs index 051028d5c4a77..df17f853b3b35 100644 --- a/src/librustc_back/target/openbsd_base.rs +++ b/src/librustc_back/target/openbsd_base.rs @@ -34,6 +34,7 @@ pub fn opts() -> TargetOptions { is_like_openbsd: true, pre_link_args: args, position_independent_executables: true, + full_relro: true, .. Default::default() } } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index a4bbdef82f094..238b7fd2e19bc 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -1029,6 +1029,10 @@ fn link_args(cmd: &mut Linker, } } + if t.options.full_relro { + cmd.full_relro(); + } + // Pass optimization flags down to the linker. cmd.optimize(); diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 0b15886083a4e..025b57956594a 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -104,6 +104,7 @@ pub trait Linker { fn add_object(&mut self, path: &Path); fn gc_sections(&mut self, keep_metadata: bool); fn position_independent_executable(&mut self); + fn full_relro(&mut self); fn optimize(&mut self); fn debuginfo(&mut self); fn no_default_libraries(&mut self); @@ -175,6 +176,7 @@ impl<'a> Linker for GccLinker<'a> { fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } fn args(&mut self, args: &[String]) { self.cmd.args(args); } fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { @@ -428,6 +430,10 @@ impl<'a> Linker for MsvcLinker<'a> { // noop } + fn full_relro(&mut self) { + // noop + } + fn no_default_libraries(&mut self) { // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC // as there's been trouble in the past of linking the C++ standard @@ -595,6 +601,10 @@ impl<'a> Linker for EmLinker<'a> { // noop } + fn full_relro(&mut self) { + // noop + } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } From 94b9cc90fb72e6be42b14559c586e3490385d045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20L=C3=B6thberg?= Date: Fri, 14 Jul 2017 22:01:37 +0200 Subject: [PATCH 2/5] Support both partial and full RELRO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Johannes Löthberg --- src/librustc/session/config.rs | 28 ++++++++++++++++++--- src/librustc_back/lib.rs | 27 ++++++++++++++++++++ src/librustc_back/target/bitrig_base.rs | 4 +-- src/librustc_back/target/dragonfly_base.rs | 4 +-- src/librustc_back/target/freebsd_base.rs | 4 +-- src/librustc_back/target/haiku_base.rs | 4 +-- src/librustc_back/target/linux_base.rs | 4 +-- src/librustc_back/target/mod.rs | 29 ++++++++++++++++------ src/librustc_back/target/netbsd_base.rs | 4 +-- src/librustc_back/target/openbsd_base.rs | 4 +-- src/librustc_trans/back/link.rs | 16 +++++++++--- src/librustc_trans/back/linker.rs | 10 ++++++++ 12 files changed, 110 insertions(+), 28 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 4b41572c1a104..83734c926ee17 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -19,7 +19,7 @@ pub use self::DebugInfoLevel::*; use session::{early_error, early_warn, Session}; use session::search_paths::SearchPaths; -use rustc_back::{LinkerFlavor, PanicStrategy}; +use rustc_back::{LinkerFlavor, PanicStrategy, RelroLevel}; use rustc_back::target::Target; use lint; use middle::cstore; @@ -654,6 +654,8 @@ macro_rules! options { Some("a number"); pub const parse_panic_strategy: Option<&'static str> = Some("either `panic` or `abort`"); + pub const parse_relro_level: Option<&'static str> = + Some("one of: `full`, `partial`, or `off`"); pub const parse_sanitizer: Option<&'static str> = Some("one of: `address`, `leak`, `memory` or `thread`"); pub const parse_linker_flavor: Option<&'static str> = @@ -665,7 +667,7 @@ macro_rules! options { #[allow(dead_code)] mod $mod_set { use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer}; - use rustc_back::{LinkerFlavor, PanicStrategy}; + use rustc_back::{LinkerFlavor, PanicStrategy, RelroLevel}; $( pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool { @@ -786,6 +788,16 @@ macro_rules! options { true } + fn parse_relro_level(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some("full") => *slot = Some(RelroLevel::Full), + Some("partial") => *slot = Some(RelroLevel::Partial), + Some("off") => *slot = Some(RelroLevel::Off), + _ => return false + } + true + } + fn parse_sanitizer(slote: &mut Option, v: Option<&str>) -> bool { match v { Some("address") => *slote = Some(Sanitizer::Address), @@ -869,6 +881,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "disable the use of the redzone"), relocation_model: Option = (None, parse_opt_string, [TRACKED], "choose the relocation model to use (rustc --print relocation-models for details)"), + relro_level: Option = (None, parse_relro_level, [TRACKED], + "choose which RELRO level to use"), code_model: Option = (None, parse_opt_string, [TRACKED], "choose the code model to use (rustc --print code-models for details)"), metadata: Vec = (Vec::new(), parse_list, [TRACKED], @@ -1776,7 +1790,7 @@ mod dep_tracking { use super::{Passes, CrateType, OptLevel, DebugInfoLevel, OutputTypes, Externs, ErrorOutputType, Sanitizer}; use syntax::feature_gate::UnstableFeatures; - use rustc_back::PanicStrategy; + use rustc_back::{PanicStrategy, RelroLevel}; pub trait DepTrackingHash { fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType); @@ -1818,11 +1832,13 @@ mod dep_tracking { impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(Option); impl_dep_tracking_hash_via_hash!(CrateType); impl_dep_tracking_hash_via_hash!(PanicStrategy); + impl_dep_tracking_hash_via_hash!(RelroLevel); impl_dep_tracking_hash_via_hash!(Passes); impl_dep_tracking_hash_via_hash!(OptLevel); impl_dep_tracking_hash_via_hash!(DebugInfoLevel); @@ -1904,7 +1920,7 @@ mod tests { use std::path::PathBuf; use std::rc::Rc; use super::{OutputType, OutputTypes, Externs}; - use rustc_back::PanicStrategy; + use rustc_back::{PanicStrategy, RelroLevel}; use syntax::symbol::Symbol; fn optgroups() -> getopts::Options { @@ -2434,6 +2450,10 @@ mod tests { opts.cg.relocation_model = Some(String::from("relocation model")); assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + opts = reference.clone(); + opts.cg.relro_level = Some(RelroLevel::Full); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + opts = reference.clone(); opts.cg.code_model = Some(String::from("code model")); assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index c776f28ecd0ba..7ec9b77af4bfc 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -114,3 +114,30 @@ impl ToJson for PanicStrategy { } } } + +#[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)] +pub enum RelroLevel { + Full, + Partial, + Off, +} + +impl RelroLevel { + pub fn desc(&self) -> &str { + match *self { + RelroLevel::Full => "full", + RelroLevel::Partial => "partial", + RelroLevel::Off => "off", + } + } +} + +impl ToJson for RelroLevel { + fn to_json(&self) -> Json { + match *self { + RelroLevel::Full => "full".to_json(), + RelroLevel::Partial => "partial".to_json(), + RelroLevel::Off => "off".to_json(), + } + } +} diff --git a/src/librustc_back/target/bitrig_base.rs b/src/librustc_back/target/bitrig_base.rs index 5a0ab83cd7231..45ceb2d5a6046 100644 --- a/src/librustc_back/target/bitrig_base.rs +++ b/src/librustc_back/target/bitrig_base.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use target::TargetOptions; +use target::{TargetOptions, RelroLevel}; use std::default::Default; pub fn opts() -> TargetOptions { @@ -19,7 +19,7 @@ pub fn opts() -> TargetOptions { linker_is_gnu: true, has_rpath: true, position_independent_executables: true, - full_relro: true, + relro_level: RelroLevel::Full, .. Default::default() } diff --git a/src/librustc_back/target/dragonfly_base.rs b/src/librustc_back/target/dragonfly_base.rs index ca116e82379b5..21dca99aa5005 100644 --- a/src/librustc_back/target/dragonfly_base.rs +++ b/src/librustc_back/target/dragonfly_base.rs @@ -9,7 +9,7 @@ // except according to those terms. use LinkerFlavor; -use target::{LinkArgs, TargetOptions}; +use target::{LinkArgs, TargetOptions, RelroLevel}; use std::default::Default; pub fn opts() -> TargetOptions { @@ -33,7 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, - full_relro: true, + relro_level: RelroLevel::Full, exe_allocation_crate: super::maybe_jemalloc(), .. Default::default() } diff --git a/src/librustc_back/target/freebsd_base.rs b/src/librustc_back/target/freebsd_base.rs index ca116e82379b5..21dca99aa5005 100644 --- a/src/librustc_back/target/freebsd_base.rs +++ b/src/librustc_back/target/freebsd_base.rs @@ -9,7 +9,7 @@ // except according to those terms. use LinkerFlavor; -use target::{LinkArgs, TargetOptions}; +use target::{LinkArgs, TargetOptions, RelroLevel}; use std::default::Default; pub fn opts() -> TargetOptions { @@ -33,7 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, - full_relro: true, + relro_level: RelroLevel::Full, exe_allocation_crate: super::maybe_jemalloc(), .. Default::default() } diff --git a/src/librustc_back/target/haiku_base.rs b/src/librustc_back/target/haiku_base.rs index c52b28708c36d..21410dcd41264 100644 --- a/src/librustc_back/target/haiku_base.rs +++ b/src/librustc_back/target/haiku_base.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use target::TargetOptions; +use target::{TargetOptions, RelroLevel}; use std::default::Default; pub fn opts() -> TargetOptions { @@ -18,7 +18,7 @@ pub fn opts() -> TargetOptions { executables: true, has_rpath: false, target_family: Some("unix".to_string()), - full_relro: true, + relro_level: RelroLevel::Full, linker_is_gnu: true, no_integrated_as: true, .. Default::default() diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs index e4e7f062f88f6..52f700ac7519f 100644 --- a/src/librustc_back/target/linux_base.rs +++ b/src/librustc_back/target/linux_base.rs @@ -9,7 +9,7 @@ // except according to those terms. use LinkerFlavor; -use target::{LinkArgs, TargetOptions}; +use target::{LinkArgs, TargetOptions, RelroLevel}; use std::default::Default; pub fn opts() -> TargetOptions { @@ -36,7 +36,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, - full_relro: true, + relro_level: RelroLevel::Full, exe_allocation_crate: super::maybe_jemalloc(), has_elf_tls: true, .. Default::default() diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 673c01f4b7e7d..a07f5d154a184 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -50,7 +50,7 @@ use std::default::Default; use std::io::prelude::*; use syntax::abi::{Abi, lookup as lookup_abi}; -use {LinkerFlavor, PanicStrategy}; +use {LinkerFlavor, PanicStrategy, RelroLevel}; mod android_base; mod apple_base; @@ -367,9 +367,10 @@ pub struct TargetOptions { /// the functions in the executable are not randomized and can be used /// during an exploit of a vulnerability in any code. pub position_independent_executables: bool, - /// Full RELRO makes the dynamic linker resolve all symbols at startup and marks the GOT - /// read-only before starting the program, preventing overwriting the GOT. - pub full_relro: bool, + /// Either partial, full, or off. Full RELRO makes the dynamic linker + /// resolve all symbols at startup and marks the GOT read-only before + /// starting the program, preventing overwriting the GOT. + pub relro_level: RelroLevel, /// Format that archives should be emitted in. This affects whether we use /// LLVM to assemble an archive or fall back to the system linker, and /// currently only "gnu" is used to fall into LLVM. Unknown strings cause @@ -457,7 +458,7 @@ impl Default for TargetOptions { has_rpath: false, no_default_libraries: true, position_independent_executables: false, - full_relro: false, + relro_level: RelroLevel::Off, pre_link_objects_exe: Vec::new(), pre_link_objects_dll: Vec::new(), post_link_objects: Vec::new(), @@ -584,6 +585,20 @@ impl Target { Some(Ok(())) })).unwrap_or(Ok(())) } ); + ($key_name:ident, RelroLevel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s { + "full" => base.options.$key_name = RelroLevel::Full, + "partial" => base.options.$key_name = RelroLevel::Partial, + "off" => base.options.$key_name = RelroLevel::Off, + _ => return Some(Err(format!("'{}' is not a valid value for \ + relro-level. Use 'full', 'partial, or 'off'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, list) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).map(|o| o.as_array() @@ -687,7 +702,7 @@ impl Target { key!(has_rpath, bool); key!(no_default_libraries, bool); key!(position_independent_executables, bool); - key!(full_relro, bool); + try!(key!(relro_level, RelroLevel)); key!(archive_format); key!(allow_asm, bool); key!(custom_unwind_resume, bool); @@ -875,7 +890,7 @@ impl ToJson for Target { target_option_val!(has_rpath); target_option_val!(no_default_libraries); target_option_val!(position_independent_executables); - target_option_val!(full_relro); + target_option_val!(relro_level); target_option_val!(archive_format); target_option_val!(allow_asm); target_option_val!(custom_unwind_resume); diff --git a/src/librustc_back/target/netbsd_base.rs b/src/librustc_back/target/netbsd_base.rs index 1d7d1b36008aa..1cb311371938e 100644 --- a/src/librustc_back/target/netbsd_base.rs +++ b/src/librustc_back/target/netbsd_base.rs @@ -9,7 +9,7 @@ // except according to those terms. use LinkerFlavor; -use target::{LinkArgs, TargetOptions}; +use target::{LinkArgs, TargetOptions, RelroLevel}; use std::default::Default; pub fn opts() -> TargetOptions { @@ -33,7 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: true, - full_relro: true, + relro_level: RelroLevel::Full, .. Default::default() } } diff --git a/src/librustc_back/target/openbsd_base.rs b/src/librustc_back/target/openbsd_base.rs index df17f853b3b35..a5f8e7ae5f91b 100644 --- a/src/librustc_back/target/openbsd_base.rs +++ b/src/librustc_back/target/openbsd_base.rs @@ -9,7 +9,7 @@ // except according to those terms. use LinkerFlavor; -use target::{LinkArgs, TargetOptions}; +use target::{LinkArgs, TargetOptions, RelroLevel}; use std::default::Default; pub fn opts() -> TargetOptions { @@ -34,7 +34,7 @@ pub fn opts() -> TargetOptions { is_like_openbsd: true, pre_link_args: args, position_independent_executables: true, - full_relro: true, + relro_level: RelroLevel::Full, .. Default::default() } } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 238b7fd2e19bc..e4ef0c73c539e 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -27,7 +27,7 @@ use rustc::dep_graph::{DepKind, DepNode}; use rustc::hir::def_id::CrateNum; use rustc::hir::svh::Svh; use rustc_back::tempdir::TempDir; -use rustc_back::PanicStrategy; +use rustc_back::{PanicStrategy, RelroLevel}; use rustc_incremental::IncrementalHashesMap; use context::get_reloc_model; use llvm; @@ -1029,8 +1029,18 @@ fn link_args(cmd: &mut Linker, } } - if t.options.full_relro { - cmd.full_relro(); + let relro_level = match sess.opts.cg.relro_level { + Some(level) => level, + None => t.options.relro_level, + }; + match relro_level { + RelroLevel::Full => { + cmd.full_relro(); + }, + RelroLevel::Partial => { + cmd.partial_relro(); + }, + RelroLevel::Off => {}, } // Pass optimization flags down to the linker. diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 025b57956594a..89ebfd0d254ec 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -104,6 +104,7 @@ pub trait Linker { fn add_object(&mut self, path: &Path); fn gc_sections(&mut self, keep_metadata: bool); fn position_independent_executable(&mut self); + fn partial_relro(&mut self); fn full_relro(&mut self); fn optimize(&mut self); fn debuginfo(&mut self); @@ -176,6 +177,7 @@ impl<'a> Linker for GccLinker<'a> { fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } fn args(&mut self, args: &[String]) { self.cmd.args(args); } @@ -430,6 +432,10 @@ impl<'a> Linker for MsvcLinker<'a> { // noop } + fn partial_relro(&mut self) { + // noop + } + fn full_relro(&mut self) { // noop } @@ -601,6 +607,10 @@ impl<'a> Linker for EmLinker<'a> { // noop } + fn partial_relro(&mut self) { + // noop + } + fn full_relro(&mut self) { // noop } From ecf3f6d4def2b2aa4b268ed50a7799ce40423bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20L=C3=B6thberg?= Date: Fri, 14 Jul 2017 22:06:36 +0200 Subject: [PATCH 3/5] Make partial RELRO default on ppc64 due to segfault MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On at least RHEL6 there is a segfault caused by the older ld.so version when BIND_NOW is used, so use partial RELRO by default on ppc64 architectures for now. Signed-off-by: Johannes Löthberg --- src/librustc_back/target/powerpc64_unknown_linux_gnu.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs index 718a79a685e06..7b038ac007396 100644 --- a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs @@ -9,7 +9,7 @@ // except according to those terms. use LinkerFlavor; -use target::{Target, TargetResult}; +use target::{Target, TargetResult, RelroLevel}; pub fn target() -> TargetResult { let mut base = super::linux_base::opts(); @@ -17,6 +17,10 @@ pub fn target() -> TargetResult { base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); + // ld.so in at least RHEL6 on ppc64 has a bug related to BIND_NOW, so only enable partial RELRO + // for now. https://github.com/rust-lang/rust/pull/43170#issuecomment-315411474 + base.relro_level = RelroLevel::Partial; + // see #36994 base.exe_allocation_crate = None; From 6a8328cfa30543614cb776dd66334bc0f8f2cdfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20L=C3=B6thberg?= Date: Tue, 18 Jul 2017 00:18:00 +0200 Subject: [PATCH 4/5] Move relro_level from CodegenOptions to DebuggingOptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Johannes Löthberg --- src/librustc/session/config.rs | 12 ++++++------ src/librustc_trans/back/link.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 83734c926ee17..a5fa6c845bb53 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -881,8 +881,6 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "disable the use of the redzone"), relocation_model: Option = (None, parse_opt_string, [TRACKED], "choose the relocation model to use (rustc --print relocation-models for details)"), - relro_level: Option = (None, parse_relro_level, [TRACKED], - "choose which RELRO level to use"), code_model: Option = (None, parse_opt_string, [TRACKED], "choose the code model to use (rustc --print code-models for details)"), metadata: Vec = (Vec::new(), parse_list, [TRACKED], @@ -1057,6 +1055,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "extra arguments to prepend to the linker invocation (space separated)"), profile: bool = (false, parse_bool, [TRACKED], "insert profiling code"), + relro_level: Option = (None, parse_relro_level, [TRACKED], + "choose which RELRO level to use"), } pub fn default_lib_output() -> CrateType { @@ -2450,10 +2450,6 @@ mod tests { opts.cg.relocation_model = Some(String::from("relocation model")); assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - opts = reference.clone(); - opts.cg.relro_level = Some(RelroLevel::Full); - assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); - opts = reference.clone(); opts.cg.code_model = Some(String::from("code model")); assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); @@ -2602,5 +2598,9 @@ mod tests { opts = reference.clone(); opts.debugging_opts.mir_opt_level = 3; assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.relro_level = Some(RelroLevel::Full); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); } } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index e4ef0c73c539e..40f3e0109284f 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -1029,7 +1029,7 @@ fn link_args(cmd: &mut Linker, } } - let relro_level = match sess.opts.cg.relro_level { + let relro_level = match sess.opts.debugging_opts.relro_level { Some(level) => level, None => t.options.relro_level, }; From 2161fb25ca9986c11212e447d88da592bcf736ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20L=C3=B6thberg?= Date: Tue, 18 Jul 2017 01:27:55 +0200 Subject: [PATCH 5/5] Implement FromStr for RelroLevel rather than duplicating the match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Johannes Löthberg --- src/librustc/session/config.rs | 9 ++++++--- src/librustc_back/lib.rs | 15 +++++++++++++++ src/librustc_back/target/mod.rs | 6 ++---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index a5fa6c845bb53..5661c4123022d 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -790,9 +790,12 @@ macro_rules! options { fn parse_relro_level(slot: &mut Option, v: Option<&str>) -> bool { match v { - Some("full") => *slot = Some(RelroLevel::Full), - Some("partial") => *slot = Some(RelroLevel::Partial), - Some("off") => *slot = Some(RelroLevel::Off), + Some(s) => { + match s.parse::() { + Ok(level) => *slot = Some(level), + _ => return false + } + }, _ => return false } true diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 7ec9b77af4bfc..55b39f226701f 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -47,6 +47,8 @@ pub mod target; pub mod slice; pub mod dynamic_lib; +use std::str::FromStr; + use serialize::json::{Json, ToJson}; macro_rules! linker_flavor { @@ -132,6 +134,19 @@ impl RelroLevel { } } +impl FromStr for RelroLevel { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "full" => Ok(RelroLevel::Full), + "partial" => Ok(RelroLevel::Partial), + "off" => Ok(RelroLevel::Off), + _ => Err(()), + } + } +} + impl ToJson for RelroLevel { fn to_json(&self) -> Json { match *self { diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index a07f5d154a184..0dbfdb4d809e0 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -588,10 +588,8 @@ impl Target { ($key_name:ident, RelroLevel) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { - match s { - "full" => base.options.$key_name = RelroLevel::Full, - "partial" => base.options.$key_name = RelroLevel::Partial, - "off" => base.options.$key_name = RelroLevel::Off, + match s.parse::() { + Ok(level) => base.options.$key_name = level, _ => return Some(Err(format!("'{}' is not a valid value for \ relro-level. Use 'full', 'partial, or 'off'.", s))),