diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 4b41572c1a104..5661c4123022d 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,19 @@ macro_rules! options { true } + fn parse_relro_level(slot: &mut Option, v: Option<&str>) -> bool { + match v { + Some(s) => { + match s.parse::() { + Ok(level) => *slot = Some(level), + _ => return false + } + }, + _ => return false + } + true + } + fn parse_sanitizer(slote: &mut Option, v: Option<&str>) -> bool { match v { Some("address") => *slote = Some(Sanitizer::Address), @@ -1043,6 +1058,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 { @@ -1776,7 +1793,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 +1835,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 +1923,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 { @@ -2582,5 +2601,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_back/lib.rs b/src/librustc_back/lib.rs index c776f28ecd0ba..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 { @@ -114,3 +116,43 @@ 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 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 { + 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 5c4e01886a434..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,6 +19,7 @@ pub fn opts() -> TargetOptions { linker_is_gnu: true, has_rpath: true, position_independent_executables: 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 e44cd393289be..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,6 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: 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 e44cd393289be..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,6 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: 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 8e7f463563c38..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,6 +18,7 @@ pub fn opts() -> TargetOptions { executables: true, has_rpath: false, target_family: Some("unix".to_string()), + 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 722d2fa16ef7a..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,6 +36,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: 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 edbbcf6f0b647..0dbfdb4d809e0 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,6 +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, + /// 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 @@ -454,6 +458,7 @@ impl Default for TargetOptions { has_rpath: false, no_default_libraries: true, position_independent_executables: false, + relro_level: RelroLevel::Off, pre_link_objects_exe: Vec::new(), pre_link_objects_dll: Vec::new(), post_link_objects: Vec::new(), @@ -580,6 +585,18 @@ 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.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))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); ($key_name:ident, list) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).map(|o| o.as_array() @@ -683,6 +700,7 @@ impl Target { key!(has_rpath, bool); key!(no_default_libraries, bool); key!(position_independent_executables, bool); + try!(key!(relro_level, RelroLevel)); key!(archive_format); key!(allow_asm, bool); key!(custom_unwind_resume, bool); @@ -870,6 +888,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!(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 63245fcae767b..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,6 +33,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, pre_link_args: args, position_independent_executables: 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 051028d5c4a77..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,6 +34,7 @@ pub fn opts() -> TargetOptions { is_like_openbsd: true, pre_link_args: args, position_independent_executables: true, + relro_level: RelroLevel::Full, .. Default::default() } } 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; diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index a4bbdef82f094..40f3e0109284f 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,6 +1029,20 @@ fn link_args(cmd: &mut Linker, } } + let relro_level = match sess.opts.debugging_opts.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. cmd.optimize(); diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 0b15886083a4e..89ebfd0d254ec 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -104,6 +104,8 @@ 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); fn no_default_libraries(&mut self); @@ -175,6 +177,8 @@ 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); } fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { @@ -428,6 +432,14 @@ impl<'a> Linker for MsvcLinker<'a> { // noop } + fn partial_relro(&mut self) { + // 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 +607,14 @@ impl<'a> Linker for EmLinker<'a> { // noop } + fn partial_relro(&mut self) { + // noop + } + + fn full_relro(&mut self) { + // noop + } + fn args(&mut self, args: &[String]) { self.cmd.args(args); }