Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add codegen option for using LLVM stack smash protection #84197

Merged
merged 1 commit into from
Nov 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_session::config::OptLevel;
use rustc_session::Session;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType};
use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector};

use crate::attributes;
use crate::llvm::AttributePlace::Function;
Expand Down Expand Up @@ -161,6 +161,17 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
}
}

fn set_stackprotector(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
let sspattr = match cx.sess().stack_protector() {
StackProtector::None => return,
StackProtector::All => Attribute::StackProtectReq,
StackProtector::Strong => Attribute::StackProtectStrong,
StackProtector::Basic => Attribute::StackProtect,
};

sspattr.apply_llfn(Function, llfn)
}

pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
let target_cpu = SmallCStr::new(llvm_util::target_cpu(cx.tcx.sess));
llvm::AddFunctionAttrStringValue(
Expand Down Expand Up @@ -271,6 +282,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
set_frame_pointer_type(cx, llfn);
set_instrument_function(cx, llfn);
set_probestack(cx, llfn);
set_stackprotector(cx, llfn);

if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) {
Attribute::Cold.apply_llfn(Function, llfn);
Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_codegen_llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,31 @@ impl CodegenBackend for LlvmCodegenBackend {
}
println!();
}
PrintRequest::StackProtectorStrategies => {
println!(
r#"Available stack protector strategies:
bbjornse marked this conversation as resolved.
Show resolved Hide resolved
all
Generate stack canaries in all functions.

strong
Generate stack canaries in a function if it either:
- has a local variable of `[T; N]` type, regardless of `T` and `N`
- takes the address of a local variable.

(Note that a local variable being borrowed is not equivalent to its
address being taken: e.g. some borrows may be removed by optimization,
while by-value argument passing may be implemented with reference to a
local stack variable in the ABI.)

basic
Generate stack canaries in functions with:
- local variables of `[T; N]` type, where `T` is byte-sized and `N` > 8.

none
Do not generate stack canaries.
"#
);
}
req => llvm_util::print(req, sess),
}
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ pub enum Attribute {
InaccessibleMemOnly = 27,
SanitizeHWAddress = 28,
WillReturn = 29,
StackProtectReq = 30,
StackProtectStrong = 31,
StackProtect = 32,
}

/// LLVMIntPredicate
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,12 @@ impl RustcDefaultCalls {
println!("{}", cfg);
}
}
RelocationModels | CodeModels | TlsModels | TargetCPUs | TargetFeatures => {
RelocationModels
| CodeModels
| TlsModels
| TargetCPUs
| StackProtectorStrategies
| TargetFeatures => {
codegen_backend.print(*req, sess);
}
// Any output here interferes with Cargo's parsing of other printed output
Expand Down
13 changes: 8 additions & 5 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ use rustc_span::edition::{Edition, DEFAULT_EDITION};
use rustc_span::symbol::sym;
use rustc_span::SourceFileHashAlgorithm;
use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy};
use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, TlsModel};
use rustc_target::spec::{
RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel,
};

use std::collections::{BTreeMap, BTreeSet};
use std::iter::FromIterator;
Expand Down Expand Up @@ -713,8 +715,8 @@ fn test_debugging_options_tracking_hash() {
// This list is in alphabetical order.
tracked!(allow_features, Some(vec![String::from("lang_items")]));
tracked!(always_encode_mir, true);
tracked!(assume_incomplete_release, true);
tracked!(asm_comments, true);
tracked!(assume_incomplete_release, true);
tracked!(binary_dep_depinfo, true);
tracked!(chalk, true);
tracked!(codegen_backend, Some("abc".to_string()));
Expand All @@ -731,8 +733,8 @@ fn test_debugging_options_tracking_hash() {
tracked!(human_readable_cgu_names, true);
tracked!(inline_in_all_cgus, Some(true));
tracked!(inline_mir, Some(true));
tracked!(inline_mir_threshold, Some(123));
tracked!(inline_mir_hint_threshold, Some(123));
tracked!(inline_mir_threshold, Some(123));
tracked!(instrument_coverage, Some(InstrumentCoverage::All));
tracked!(instrument_mcount, true);
tracked!(link_only, true);
Expand Down Expand Up @@ -764,23 +766,24 @@ fn test_debugging_options_tracking_hash() {
tracked!(relax_elf_relocations, Some(true));
tracked!(relro_level, Some(RelroLevel::Full));
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc")));
tracked!(report_delayed_bugs, true);
tracked!(sanitizer, SanitizerSet::ADDRESS);
tracked!(sanitizer_memory_track_origins, 2);
tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
tracked!(saturating_float_casts, Some(true));
tracked!(share_generics, Some(true));
tracked!(show_span, Some(String::from("abc")));
tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc")));
tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
tracked!(stack_protector, StackProtector::All);
tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));
tracked!(teach, true);
tracked!(thinlto, Some(true));
tracked!(thir_unsafeck, true);
tracked!(tune_cpu, Some(String::from("abc")));
tracked!(tls_model, Some(TlsModel::GeneralDynamic));
tracked!(trap_unreachable, Some(false));
tracked!(treat_err_as_bug, NonZeroUsize::new(1));
tracked!(tune_cpu, Some(String::from("abc")));
tracked!(unleash_the_miri_inside_of_you, true);
tracked!(use_ctors_section, Some(true));
tracked!(verify_llvm_ir, true);
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ enum LLVMRustAttribute {
InaccessibleMemOnly = 27,
SanitizeHWAddress = 28,
WillReturn = 29,
StackProtectReq = 30,
StackProtectStrong = 31,
StackProtect = 32,
};

typedef struct OpaqueRustString *RustStringRef;
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
return Attribute::SanitizeHWAddress;
case WillReturn:
return Attribute::WillReturn;
case StackProtectReq:
return Attribute::StackProtectReq;
case StackProtectStrong:
return Attribute::StackProtectStrong;
case StackProtect:
return Attribute::StackProtect;
}
report_fatal_error("bad AttributeKind");
}
Expand Down
11 changes: 8 additions & 3 deletions compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ pub enum PrintRequest {
TlsModels,
TargetSpec,
NativeStaticLibs,
StackProtectorStrategies,
}

#[derive(Copy, Clone)]
Expand Down Expand Up @@ -1110,8 +1111,8 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
"print",
"Compiler information to print on stdout",
"[crate-name|file-names|sysroot|target-libdir|cfg|target-list|\
target-cpus|target-features|relocation-models|\
code-models|tls-models|target-spec-json|native-static-libs]",
target-cpus|target-features|relocation-models|code-models|\
tls-models|target-spec-json|native-static-libs|stack-protector-strategies]",
),
opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"),
opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"),
Expand Down Expand Up @@ -1527,6 +1528,7 @@ fn collect_print_requests(
"code-models" => PrintRequest::CodeModels,
"tls-models" => PrintRequest::TlsModels,
"native-static-libs" => PrintRequest::NativeStaticLibs,
"stack-protector-strategies" => PrintRequest::StackProtectorStrategies,
"target-spec-json" => {
if dopts.unstable_options {
PrintRequest::TargetSpec
Expand Down Expand Up @@ -2494,7 +2496,9 @@ crate mod dep_tracking {
use rustc_span::edition::Edition;
use rustc_span::RealFileName;
use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, TargetTriple, TlsModel};
use rustc_target::spec::{
RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
};
use std::collections::hash_map::DefaultHasher;
use std::collections::BTreeMap;
use std::hash::Hash;
Expand Down Expand Up @@ -2568,6 +2572,7 @@ crate mod dep_tracking {
Edition,
LinkerPluginLto,
SplitDebuginfo,
StackProtector,
SwitchWithOptPath,
SymbolManglingVersion,
SourceFileHashAlgorithm,
Expand Down
16 changes: 15 additions & 1 deletion compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use crate::lint;
use crate::search_paths::SearchPath;
use crate::utils::NativeLib;
use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet};
use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TargetTriple, TlsModel};
use rustc_target::spec::{
RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
};

use rustc_feature::UnstableFeatures;
use rustc_span::edition::Edition;
Expand Down Expand Up @@ -385,6 +387,8 @@ mod desc {
pub const parse_split_debuginfo: &str =
"one of supported split-debuginfo modes (`off`, `packed`, or `unpacked`)";
pub const parse_gcc_ld: &str = "one of: no value, `lld`";
pub const parse_stack_protector: &str =
"one of (`none` (default), `basic`, `strong`, or `all`)";
}

mod parse {
Expand Down Expand Up @@ -917,6 +921,14 @@ mod parse {
}
true
}

crate fn parse_stack_protector(slot: &mut StackProtector, v: Option<&str>) -> bool {
match v.and_then(|s| StackProtector::from_str(s).ok()) {
Some(ssp) => *slot = ssp,
_ => return false,
}
true
}
}

options! {
Expand Down Expand Up @@ -1330,6 +1342,8 @@ options! {
"exclude spans when debug-printing compiler state (default: no)"),
src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
"hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED],
"control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED],
Expand Down
21 changes: 20 additions & 1 deletion compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, S
use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
use rustc_target::asm::InlineAsmArch;
use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel};
use rustc_target::spec::{SanitizerSet, SplitDebuginfo, Target, TargetTriple, TlsModel};
use rustc_target::spec::{
SanitizerSet, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel,
};

use std::cell::{self, RefCell};
use std::env;
Expand Down Expand Up @@ -732,6 +734,14 @@ impl Session {
self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
}

pub fn stack_protector(&self) -> StackProtector {
if self.target.options.supports_stack_protector {
self.opts.debugging_opts.stack_protector
} else {
StackProtector::None
}
}

pub fn target_can_use_split_dwarf(&self) -> bool {
!self.target.is_like_windows && !self.target.is_like_osx
}
Expand Down Expand Up @@ -1411,6 +1421,15 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
sess.err("`-Zsanitizer=cfi` requires `-Clto`");
}
}

if sess.opts.debugging_opts.stack_protector != StackProtector::None {
if !sess.target.options.supports_stack_protector {
sess.warn(&format!(
"`-Z stack-protector={}` is not supported for target {} and will be ignored",
sess.opts.debugging_opts.stack_protector, sess.opts.target_triple
))
}
}
}

/// Holds data on the current incremental compilation session, if there is one.
Expand Down
60 changes: 60 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,59 @@ impl ToJson for FramePointer {
}
}

/// Controls use of stack canaries.
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
pub enum StackProtector {
/// Disable stack canary generation.
None,

/// On LLVM, mark all generated LLVM functions with the `ssp` attribute (see
/// llvm/docs/LangRef.rst). This triggers stack canary generation in
/// functions which contain an array of a byte-sized type with more than
/// eight elements.
Basic,

/// On LLVM, mark all generated LLVM functions with the `sspstrong`
/// attribute (see llvm/docs/LangRef.rst). This triggers stack canary
/// generation in functions which either contain an array, or which take
/// the address of a local variable.
Strong,

/// Generate stack canaries in all functions.
All,
}

impl StackProtector {
fn as_str(&self) -> &'static str {
match self {
StackProtector::None => "none",
StackProtector::Basic => "basic",
StackProtector::Strong => "strong",
StackProtector::All => "all",
}
}
}

impl FromStr for StackProtector {
type Err = ();

fn from_str(s: &str) -> Result<StackProtector, ()> {
Ok(match s {
"none" => StackProtector::None,
"basic" => StackProtector::Basic,
"strong" => StackProtector::Strong,
"all" => StackProtector::All,
_ => return Err(()),
})
}
}

impl fmt::Display for StackProtector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}

macro_rules! supported_targets {
( $(($( $triple:literal, )+ $module:ident ),)+ ) => {
$(mod $module;)+
Expand Down Expand Up @@ -1360,6 +1413,10 @@ pub struct TargetOptions {

/// Whether or not the DWARF `.debug_aranges` section should be generated.
pub generate_arange_section: bool,

/// Whether the target supports stack canary checks. `true` by default,
/// since this is most common among tier 1 and tier 2 targets.
pub supports_stack_protector: bool,
}

impl Default for TargetOptions {
Expand Down Expand Up @@ -1466,6 +1523,7 @@ impl Default for TargetOptions {
default_adjusted_cabi: None,
c_enum_min_bits: 32,
generate_arange_section: true,
supports_stack_protector: true,
}
}
}
Expand Down Expand Up @@ -2052,6 +2110,7 @@ impl Target {
key!(default_adjusted_cabi, Option<Abi>)?;
key!(c_enum_min_bits, u64);
key!(generate_arange_section, bool);
key!(supports_stack_protector, bool);

if base.is_builtin {
// This can cause unfortunate ICEs later down the line.
Expand Down Expand Up @@ -2292,6 +2351,7 @@ impl ToJson for Target {
target_option_val!(supported_sanitizers);
target_option_val!(c_enum_min_bits);
target_option_val!(generate_arange_section);
target_option_val!(supports_stack_protector);

if let Some(abi) = self.default_adjusted_cabi {
d.insert("default-adjusted-cabi".to_string(), Abi::name(abi).to_json());
Expand Down
Loading