Skip to content

Commit 4e56bbe

Browse files
committed
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 #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.
2 parents 88e2c39 + 2161fb2 commit 4e56bbe

File tree

13 files changed

+143
-14
lines changed

13 files changed

+143
-14
lines changed

Diff for: src/librustc/session/config.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub use self::DebugInfoLevel::*;
1919
use session::{early_error, early_warn, Session};
2020
use session::search_paths::SearchPaths;
2121

22-
use rustc_back::{LinkerFlavor, PanicStrategy};
22+
use rustc_back::{LinkerFlavor, PanicStrategy, RelroLevel};
2323
use rustc_back::target::Target;
2424
use lint;
2525
use middle::cstore;
@@ -654,6 +654,8 @@ macro_rules! options {
654654
Some("a number");
655655
pub const parse_panic_strategy: Option<&'static str> =
656656
Some("either `panic` or `abort`");
657+
pub const parse_relro_level: Option<&'static str> =
658+
Some("one of: `full`, `partial`, or `off`");
657659
pub const parse_sanitizer: Option<&'static str> =
658660
Some("one of: `address`, `leak`, `memory` or `thread`");
659661
pub const parse_linker_flavor: Option<&'static str> =
@@ -665,7 +667,7 @@ macro_rules! options {
665667
#[allow(dead_code)]
666668
mod $mod_set {
667669
use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer};
668-
use rustc_back::{LinkerFlavor, PanicStrategy};
670+
use rustc_back::{LinkerFlavor, PanicStrategy, RelroLevel};
669671

670672
$(
671673
pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool {
@@ -786,6 +788,19 @@ macro_rules! options {
786788
true
787789
}
788790

791+
fn parse_relro_level(slot: &mut Option<RelroLevel>, v: Option<&str>) -> bool {
792+
match v {
793+
Some(s) => {
794+
match s.parse::<RelroLevel>() {
795+
Ok(level) => *slot = Some(level),
796+
_ => return false
797+
}
798+
},
799+
_ => return false
800+
}
801+
true
802+
}
803+
789804
fn parse_sanitizer(slote: &mut Option<Sanitizer>, v: Option<&str>) -> bool {
790805
match v {
791806
Some("address") => *slote = Some(Sanitizer::Address),
@@ -1043,6 +1058,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
10431058
"extra arguments to prepend to the linker invocation (space separated)"),
10441059
profile: bool = (false, parse_bool, [TRACKED],
10451060
"insert profiling code"),
1061+
relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],
1062+
"choose which RELRO level to use"),
10461063
}
10471064

10481065
pub fn default_lib_output() -> CrateType {
@@ -1776,7 +1793,7 @@ mod dep_tracking {
17761793
use super::{Passes, CrateType, OptLevel, DebugInfoLevel,
17771794
OutputTypes, Externs, ErrorOutputType, Sanitizer};
17781795
use syntax::feature_gate::UnstableFeatures;
1779-
use rustc_back::PanicStrategy;
1796+
use rustc_back::{PanicStrategy, RelroLevel};
17801797

17811798
pub trait DepTrackingHash {
17821799
fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType);
@@ -1818,11 +1835,13 @@ mod dep_tracking {
18181835
impl_dep_tracking_hash_via_hash!(Option<String>);
18191836
impl_dep_tracking_hash_via_hash!(Option<(String, u64)>);
18201837
impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>);
1838+
impl_dep_tracking_hash_via_hash!(Option<RelroLevel>);
18211839
impl_dep_tracking_hash_via_hash!(Option<lint::Level>);
18221840
impl_dep_tracking_hash_via_hash!(Option<PathBuf>);
18231841
impl_dep_tracking_hash_via_hash!(Option<cstore::NativeLibraryKind>);
18241842
impl_dep_tracking_hash_via_hash!(CrateType);
18251843
impl_dep_tracking_hash_via_hash!(PanicStrategy);
1844+
impl_dep_tracking_hash_via_hash!(RelroLevel);
18261845
impl_dep_tracking_hash_via_hash!(Passes);
18271846
impl_dep_tracking_hash_via_hash!(OptLevel);
18281847
impl_dep_tracking_hash_via_hash!(DebugInfoLevel);
@@ -1904,7 +1923,7 @@ mod tests {
19041923
use std::path::PathBuf;
19051924
use std::rc::Rc;
19061925
use super::{OutputType, OutputTypes, Externs};
1907-
use rustc_back::PanicStrategy;
1926+
use rustc_back::{PanicStrategy, RelroLevel};
19081927
use syntax::symbol::Symbol;
19091928

19101929
fn optgroups() -> getopts::Options {
@@ -2582,5 +2601,9 @@ mod tests {
25822601
opts = reference.clone();
25832602
opts.debugging_opts.mir_opt_level = 3;
25842603
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
2604+
2605+
opts = reference.clone();
2606+
opts.debugging_opts.relro_level = Some(RelroLevel::Full);
2607+
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
25852608
}
25862609
}

Diff for: src/librustc_back/lib.rs

+42
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub mod target;
4747
pub mod slice;
4848
pub mod dynamic_lib;
4949

50+
use std::str::FromStr;
51+
5052
use serialize::json::{Json, ToJson};
5153

5254
macro_rules! linker_flavor {
@@ -114,3 +116,43 @@ impl ToJson for PanicStrategy {
114116
}
115117
}
116118
}
119+
120+
#[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
121+
pub enum RelroLevel {
122+
Full,
123+
Partial,
124+
Off,
125+
}
126+
127+
impl RelroLevel {
128+
pub fn desc(&self) -> &str {
129+
match *self {
130+
RelroLevel::Full => "full",
131+
RelroLevel::Partial => "partial",
132+
RelroLevel::Off => "off",
133+
}
134+
}
135+
}
136+
137+
impl FromStr for RelroLevel {
138+
type Err = ();
139+
140+
fn from_str(s: &str) -> Result<RelroLevel, ()> {
141+
match s {
142+
"full" => Ok(RelroLevel::Full),
143+
"partial" => Ok(RelroLevel::Partial),
144+
"off" => Ok(RelroLevel::Off),
145+
_ => Err(()),
146+
}
147+
}
148+
}
149+
150+
impl ToJson for RelroLevel {
151+
fn to_json(&self) -> Json {
152+
match *self {
153+
RelroLevel::Full => "full".to_json(),
154+
RelroLevel::Partial => "partial".to_json(),
155+
RelroLevel::Off => "off".to_json(),
156+
}
157+
}
158+
}

Diff for: src/librustc_back/target/bitrig_base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use target::TargetOptions;
11+
use target::{TargetOptions, RelroLevel};
1212
use std::default::Default;
1313

1414
pub fn opts() -> TargetOptions {
@@ -19,6 +19,7 @@ pub fn opts() -> TargetOptions {
1919
linker_is_gnu: true,
2020
has_rpath: true,
2121
position_independent_executables: true,
22+
relro_level: RelroLevel::Full,
2223

2324
.. Default::default()
2425
}

Diff for: src/librustc_back/target/dragonfly_base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use LinkerFlavor;
12-
use target::{LinkArgs, TargetOptions};
12+
use target::{LinkArgs, TargetOptions, RelroLevel};
1313
use std::default::Default;
1414

1515
pub fn opts() -> TargetOptions {
@@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions {
3333
has_rpath: true,
3434
pre_link_args: args,
3535
position_independent_executables: true,
36+
relro_level: RelroLevel::Full,
3637
exe_allocation_crate: super::maybe_jemalloc(),
3738
.. Default::default()
3839
}

Diff for: src/librustc_back/target/freebsd_base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use LinkerFlavor;
12-
use target::{LinkArgs, TargetOptions};
12+
use target::{LinkArgs, TargetOptions, RelroLevel};
1313
use std::default::Default;
1414

1515
pub fn opts() -> TargetOptions {
@@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions {
3333
has_rpath: true,
3434
pre_link_args: args,
3535
position_independent_executables: true,
36+
relro_level: RelroLevel::Full,
3637
exe_allocation_crate: super::maybe_jemalloc(),
3738
.. Default::default()
3839
}

Diff for: src/librustc_back/target/haiku_base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use target::TargetOptions;
11+
use target::{TargetOptions, RelroLevel};
1212
use std::default::Default;
1313

1414
pub fn opts() -> TargetOptions {
@@ -18,6 +18,7 @@ pub fn opts() -> TargetOptions {
1818
executables: true,
1919
has_rpath: false,
2020
target_family: Some("unix".to_string()),
21+
relro_level: RelroLevel::Full,
2122
linker_is_gnu: true,
2223
no_integrated_as: true,
2324
.. Default::default()

Diff for: src/librustc_back/target/linux_base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use LinkerFlavor;
12-
use target::{LinkArgs, TargetOptions};
12+
use target::{LinkArgs, TargetOptions, RelroLevel};
1313
use std::default::Default;
1414

1515
pub fn opts() -> TargetOptions {
@@ -36,6 +36,7 @@ pub fn opts() -> TargetOptions {
3636
has_rpath: true,
3737
pre_link_args: args,
3838
position_independent_executables: true,
39+
relro_level: RelroLevel::Full,
3940
exe_allocation_crate: super::maybe_jemalloc(),
4041
has_elf_tls: true,
4142
.. Default::default()

Diff for: src/librustc_back/target/mod.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use std::default::Default;
5050
use std::io::prelude::*;
5151
use syntax::abi::{Abi, lookup as lookup_abi};
5252

53-
use {LinkerFlavor, PanicStrategy};
53+
use {LinkerFlavor, PanicStrategy, RelroLevel};
5454

5555
mod android_base;
5656
mod apple_base;
@@ -367,6 +367,10 @@ pub struct TargetOptions {
367367
/// the functions in the executable are not randomized and can be used
368368
/// during an exploit of a vulnerability in any code.
369369
pub position_independent_executables: bool,
370+
/// Either partial, full, or off. Full RELRO makes the dynamic linker
371+
/// resolve all symbols at startup and marks the GOT read-only before
372+
/// starting the program, preventing overwriting the GOT.
373+
pub relro_level: RelroLevel,
370374
/// Format that archives should be emitted in. This affects whether we use
371375
/// LLVM to assemble an archive or fall back to the system linker, and
372376
/// currently only "gnu" is used to fall into LLVM. Unknown strings cause
@@ -454,6 +458,7 @@ impl Default for TargetOptions {
454458
has_rpath: false,
455459
no_default_libraries: true,
456460
position_independent_executables: false,
461+
relro_level: RelroLevel::Off,
457462
pre_link_objects_exe: Vec::new(),
458463
pre_link_objects_dll: Vec::new(),
459464
post_link_objects: Vec::new(),
@@ -580,6 +585,18 @@ impl Target {
580585
Some(Ok(()))
581586
})).unwrap_or(Ok(()))
582587
} );
588+
($key_name:ident, RelroLevel) => ( {
589+
let name = (stringify!($key_name)).replace("_", "-");
590+
obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
591+
match s.parse::<RelroLevel>() {
592+
Ok(level) => base.options.$key_name = level,
593+
_ => return Some(Err(format!("'{}' is not a valid value for \
594+
relro-level. Use 'full', 'partial, or 'off'.",
595+
s))),
596+
}
597+
Some(Ok(()))
598+
})).unwrap_or(Ok(()))
599+
} );
583600
($key_name:ident, list) => ( {
584601
let name = (stringify!($key_name)).replace("_", "-");
585602
obj.find(&name[..]).map(|o| o.as_array()
@@ -683,6 +700,7 @@ impl Target {
683700
key!(has_rpath, bool);
684701
key!(no_default_libraries, bool);
685702
key!(position_independent_executables, bool);
703+
try!(key!(relro_level, RelroLevel));
686704
key!(archive_format);
687705
key!(allow_asm, bool);
688706
key!(custom_unwind_resume, bool);
@@ -870,6 +888,7 @@ impl ToJson for Target {
870888
target_option_val!(has_rpath);
871889
target_option_val!(no_default_libraries);
872890
target_option_val!(position_independent_executables);
891+
target_option_val!(relro_level);
873892
target_option_val!(archive_format);
874893
target_option_val!(allow_asm);
875894
target_option_val!(custom_unwind_resume);

Diff for: src/librustc_back/target/netbsd_base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use LinkerFlavor;
12-
use target::{LinkArgs, TargetOptions};
12+
use target::{LinkArgs, TargetOptions, RelroLevel};
1313
use std::default::Default;
1414

1515
pub fn opts() -> TargetOptions {
@@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions {
3333
has_rpath: true,
3434
pre_link_args: args,
3535
position_independent_executables: true,
36+
relro_level: RelroLevel::Full,
3637
.. Default::default()
3738
}
3839
}

Diff for: src/librustc_back/target/openbsd_base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use LinkerFlavor;
12-
use target::{LinkArgs, TargetOptions};
12+
use target::{LinkArgs, TargetOptions, RelroLevel};
1313
use std::default::Default;
1414

1515
pub fn opts() -> TargetOptions {
@@ -34,6 +34,7 @@ pub fn opts() -> TargetOptions {
3434
is_like_openbsd: true,
3535
pre_link_args: args,
3636
position_independent_executables: true,
37+
relro_level: RelroLevel::Full,
3738
.. Default::default()
3839
}
3940
}

Diff for: src/librustc_back/target/powerpc64_unknown_linux_gnu.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
// except according to those terms.
1010

1111
use LinkerFlavor;
12-
use target::{Target, TargetResult};
12+
use target::{Target, TargetResult, RelroLevel};
1313

1414
pub fn target() -> TargetResult {
1515
let mut base = super::linux_base::opts();
1616
base.cpu = "ppc64".to_string();
1717
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string());
1818
base.max_atomic_width = Some(64);
1919

20+
// ld.so in at least RHEL6 on ppc64 has a bug related to BIND_NOW, so only enable partial RELRO
21+
// for now. https://github.com/rust-lang/rust/pull/43170#issuecomment-315411474
22+
base.relro_level = RelroLevel::Partial;
23+
2024
// see #36994
2125
base.exe_allocation_crate = None;
2226

Diff for: src/librustc_trans/back/link.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use rustc::dep_graph::{DepKind, DepNode};
2727
use rustc::hir::def_id::CrateNum;
2828
use rustc::hir::svh::Svh;
2929
use rustc_back::tempdir::TempDir;
30-
use rustc_back::PanicStrategy;
30+
use rustc_back::{PanicStrategy, RelroLevel};
3131
use rustc_incremental::IncrementalHashesMap;
3232
use context::get_reloc_model;
3333
use llvm;
@@ -1029,6 +1029,20 @@ fn link_args(cmd: &mut Linker,
10291029
}
10301030
}
10311031

1032+
let relro_level = match sess.opts.debugging_opts.relro_level {
1033+
Some(level) => level,
1034+
None => t.options.relro_level,
1035+
};
1036+
match relro_level {
1037+
RelroLevel::Full => {
1038+
cmd.full_relro();
1039+
},
1040+
RelroLevel::Partial => {
1041+
cmd.partial_relro();
1042+
},
1043+
RelroLevel::Off => {},
1044+
}
1045+
10321046
// Pass optimization flags down to the linker.
10331047
cmd.optimize();
10341048

0 commit comments

Comments
 (0)