diff options
| author | bors <bors@rust-lang.org> | 2017-07-19 05:39:14 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-07-19 05:39:14 +0000 |
| commit | 4e56bbeb19c48edf6a211448a6fb5b67b29eceec (patch) | |
| tree | 274e665d416a6c8f93ffe0a1b0dbbfd9ec96a5f9 | |
| parent | 88e2c396a4062d59ca6be3a74ac432bd1f878942 (diff) | |
| parent | 2161fb25ca9986c11212e447d88da592bcf736ce (diff) | |
| download | rust-4e56bbeb19c48edf6a211448a6fb5b67b29eceec.tar.gz rust-4e56bbeb19c48edf6a211448a6fb5b67b29eceec.zip | |
Auto merge of #43170 - kyrias:full-relro, r=alexcrichton
Add support for full RELRO 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. --- I'm not entirely certain if this is the best way to do it, but I figured mimicking the way it's done for PIE seemed like a good start at least. I'm not sure whether we want to have it enabled by default globally and then disabling it explicitly for targets that don't support it though. I'm also not sure whether the `full_relro` function should call `bug!()` or something like it for linkers that don't support it rather than no-opping.
| -rw-r--r-- | src/librustc/session/config.rs | 31 | ||||
| -rw-r--r-- | src/librustc_back/lib.rs | 42 | ||||
| -rw-r--r-- | src/librustc_back/target/bitrig_base.rs | 3 | ||||
| -rw-r--r-- | src/librustc_back/target/dragonfly_base.rs | 3 | ||||
| -rw-r--r-- | src/librustc_back/target/freebsd_base.rs | 3 | ||||
| -rw-r--r-- | src/librustc_back/target/haiku_base.rs | 3 | ||||
| -rw-r--r-- | src/librustc_back/target/linux_base.rs | 3 | ||||
| -rw-r--r-- | src/librustc_back/target/mod.rs | 21 | ||||
| -rw-r--r-- | src/librustc_back/target/netbsd_base.rs | 3 | ||||
| -rw-r--r-- | src/librustc_back/target/openbsd_base.rs | 3 | ||||
| -rw-r--r-- | src/librustc_back/target/powerpc64_unknown_linux_gnu.rs | 6 | ||||
| -rw-r--r-- | src/librustc_trans/back/link.rs | 16 | ||||
| -rw-r--r-- | src/librustc_trans/back/linker.rs | 20 |
13 files changed, 143 insertions, 14 deletions
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 4b41572c1a1..5661c412302 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<RelroLevel>, v: Option<&str>) -> bool { + match v { + Some(s) => { + match s.parse::<RelroLevel>() { + Ok(level) => *slot = Some(level), + _ => return false + } + }, + _ => return false + } + true + } + fn parse_sanitizer(slote: &mut Option<Sanitizer>, 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<RelroLevel> = (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<String>); impl_dep_tracking_hash_via_hash!(Option<(String, u64)>); impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>); + impl_dep_tracking_hash_via_hash!(Option<RelroLevel>); impl_dep_tracking_hash_via_hash!(Option<lint::Level>); impl_dep_tracking_hash_via_hash!(Option<PathBuf>); impl_dep_tracking_hash_via_hash!(Option<cstore::NativeLibraryKind>); 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 c776f28ecd0..55b39f22670 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<RelroLevel, ()> { + 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 5c4e01886a4..45ceb2d5a60 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 e44cd393289..21dca99aa50 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 e44cd393289..21dca99aa50 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 8e7f463563c..21410dcd412 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 722d2fa16ef..52f700ac751 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 edbbcf6f0b6..0dbfdb4d809 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::<RelroLevel>() { + 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 63245fcae76..1cb31137193 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 051028d5c4a..a5f8e7ae5f9 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 718a79a685e..7b038ac0073 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 6cbdae76277..5e85771217b 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 0b15886083a..89ebfd0d254 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); } |
