Skip to content

Commit

Permalink
Auto merge of #43170 - kyrias:full-relro, r=alexcrichton
Browse files Browse the repository at this point in the history
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 #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.
  • Loading branch information
bors committed Jul 19, 2017
2 parents 88e2c39 + 2161fb2 commit 4e56bbe
Show file tree
Hide file tree
Showing 13 changed files with 143 additions and 14 deletions.
31 changes: 27 additions & 4 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> =
Expand All @@ -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 {
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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());
}
}
42 changes: 42 additions & 0 deletions src/librustc_back/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
}
}
}
3 changes: 2 additions & 1 deletion src/librustc_back/target/bitrig_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_back/target/dragonfly_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_back/target/freebsd_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_back/target/haiku_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_back/target/linux_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
Expand Down
21 changes: 20 additions & 1 deletion src/librustc_back/target/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_back/target/netbsd_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
}
}
3 changes: 2 additions & 1 deletion src/librustc_back/target/openbsd_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
}
}
6 changes: 5 additions & 1 deletion src/librustc_back/target/powerpc64_unknown_linux_gnu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@
// 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();
base.cpu = "ppc64".to_string();
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;

Expand Down
16 changes: 15 additions & 1 deletion src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down
Loading

0 comments on commit 4e56bbe

Please sign in to comment.