Skip to content

Commit 55ce98f

Browse files
committed
Add -Zstack-protector-guard target modifier
Equivalent to Clang's `-mstack-protector-guard=*`. Allows configuring how the stack canary is accessed (global, TLS, or system register), which must be consistent across the crate graph.
1 parent be8e824 commit 55ce98f

6 files changed

Lines changed: 191 additions & 4 deletions

File tree

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,46 @@ pub(crate) unsafe fn create_module<'ll>(
557557
);
558558
}
559559

560+
// Set stack-protector-guard module flags (equivalent to Clang's -mstack-protector-guard=*)
561+
if let Some(ref guard) = sess.opts.unstable_opts.stack_protector_guard {
562+
if let Some(ref mode) = guard.mode {
563+
llvm::add_module_flag_str(
564+
llmod,
565+
llvm::ModuleFlagMergeBehavior::Error,
566+
"stack-protector-guard",
567+
mode.as_str(),
568+
);
569+
}
570+
if let Some(offset) = guard.offset {
571+
llvm::add_module_flag_u32(
572+
llmod,
573+
llvm::ModuleFlagMergeBehavior::Error,
574+
"stack-protector-guard-offset",
575+
offset,
576+
);
577+
}
578+
if let Some(ref reg) = guard.reg {
579+
if !reg.is_empty() {
580+
llvm::add_module_flag_str(
581+
llmod,
582+
llvm::ModuleFlagMergeBehavior::Error,
583+
"stack-protector-guard-reg",
584+
reg,
585+
);
586+
}
587+
}
588+
if let Some(ref sym) = guard.symbol {
589+
if !sym.is_empty() {
590+
llvm::add_module_flag_str(
591+
llmod,
592+
llvm::ModuleFlagMergeBehavior::Error,
593+
"stack-protector-guard-symbol",
594+
sym,
595+
);
596+
}
597+
}
598+
}
599+
560600
// Add module flags specified via -Z llvm_module_flag
561601
for (key, value, merge_behavior) in &sess.opts.unstable_opts.llvm_module_flag {
562602
let merge_behavior = match merge_behavior.as_str() {

compiler/rustc_interface/src/tests.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ use rustc_session::config::{
1616
InstrumentCoverage, InstrumentMcount, InstrumentXRay, LinkSelfContained, LinkerPluginLto,
1717
LocationDetail, LtoCli, MirIncludeSpans, NextSolverConfig, Offload, Options, OutFileName,
1818
OutputType, OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry, Polonius,
19-
ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
20-
build_configuration, build_session_options, rustc_optgroups,
19+
ProcMacroExecutionStrategy, StackProtectorGuard, StackProtectorGuardMode, Strip,
20+
SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, build_configuration,
21+
build_session_options, rustc_optgroups,
2122
};
2223
use rustc_session::lint::Level;
2324
use rustc_session::search_paths::SearchPath;
@@ -894,6 +895,15 @@ fn test_unstable_options_tracking_hash() {
894895
tracked!(split_lto_unit, Some(true));
895896
tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
896897
tracked!(stack_protector, StackProtector::All);
898+
tracked!(
899+
stack_protector_guard,
900+
Some(StackProtectorGuard {
901+
mode: Some(StackProtectorGuardMode::Sysreg),
902+
offset: Some(0),
903+
reg: Some("sp_el0".to_string()),
904+
symbol: None,
905+
})
906+
);
897907
tracked!(staticlib_hide_internal_symbols, true);
898908
tracked!(staticlib_rename_internal_symbols, true);
899909
tracked!(teach, true);

compiler/rustc_session/src/config.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,6 +1602,31 @@ pub struct BranchProtection {
16021602
pub gcs: bool,
16031603
}
16041604

1605+
#[derive(Clone, Copy, Hash, Debug, PartialEq)]
1606+
pub enum StackProtectorGuardMode {
1607+
Global,
1608+
Tls,
1609+
Sysreg,
1610+
}
1611+
1612+
impl StackProtectorGuardMode {
1613+
pub fn as_str(&self) -> &'static str {
1614+
match self {
1615+
Self::Global => "global",
1616+
Self::Tls => "tls",
1617+
Self::Sysreg => "sysreg",
1618+
}
1619+
}
1620+
}
1621+
1622+
#[derive(Clone, Hash, Debug, PartialEq, Default)]
1623+
pub struct StackProtectorGuard {
1624+
pub mode: Option<StackProtectorGuardMode>,
1625+
pub offset: Option<u32>,
1626+
pub reg: Option<String>,
1627+
pub symbol: Option<String>,
1628+
}
1629+
16051630
pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg {
16061631
// First disallow some configuration given on the command line
16071632
cfg::disallow_cfgs(sess, &user_cfg);
@@ -3099,8 +3124,8 @@ pub(crate) mod dep_tracking {
30993124
FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentMcount, InstrumentXRay,
31003125
LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload,
31013126
OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius,
3102-
ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath,
3103-
SymbolManglingVersion, WasiExecModel,
3127+
ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, StackProtectorGuard,
3128+
StackProtectorGuardMode, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
31043129
};
31053130
use crate::lint;
31063131
use crate::utils::NativeLib;
@@ -3198,6 +3223,8 @@ pub(crate) mod dep_tracking {
31983223
LocationDetail,
31993224
FmtDebug,
32003225
BranchProtection,
3226+
StackProtectorGuard,
3227+
StackProtectorGuardMode,
32013228
LanguageIdentifier,
32023229
NextSolverConfig,
32033230
PatchableFunctionEntry,

compiler/rustc_session/src/errors.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,22 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> {
381381
pub(crate) target_triple: &'a TargetTuple,
382382
}
383383

384+
#[derive(Diagnostic)]
385+
#[diag("`-Z stack-protector-guard` is not supported for the `{$arch}` architecture")]
386+
pub(crate) struct StackProtectorGuardUnsupportedArch {
387+
pub(crate) arch: String,
388+
}
389+
390+
#[derive(Diagnostic)]
391+
#[diag(
392+
"invalid value `{$guard}` for `-Z stack-protector-guard` on `{$arch}` architecture, expected one of: {$valid}"
393+
)]
394+
pub(crate) struct StackProtectorGuardInvalidValue {
395+
pub(crate) arch: String,
396+
pub(crate) guard: String,
397+
pub(crate) valid: String,
398+
}
399+
384400
#[derive(Diagnostic)]
385401
#[diag(
386402
"`-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored"

compiler/rustc_session/src/options.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,8 @@ mod desc {
852852
pub(crate) const parse_stack_protector: &str =
853853
"one of (`none` (default), `basic`, `strong`, or `all`)";
854854
pub(crate) const parse_branch_protection: &str = "a `,` separated combination of `bti`, `gcs`, `pac-ret`, (optionally with `pc`, `b-key`, `leaf` if `pac-ret` is set)";
855+
pub(crate) const parse_stack_protector_guard: &str =
856+
"one of (`global`, `tls`, `sysreg`), optionally with `offset=N`, `reg=R`, `symbol=S`";
855857
pub(crate) const parse_proc_macro_execution_strategy: &str =
856858
"one of supported execution strategies (`same-thread`, or `cross-thread`)";
857859
pub(crate) const parse_inlining_threshold: &str =
@@ -1961,6 +1963,45 @@ pub mod parse {
19611963
true
19621964
}
19631965

1966+
pub(crate) fn parse_stack_protector_guard(
1967+
slot: &mut Option<StackProtectorGuard>,
1968+
v: Option<&str>,
1969+
) -> bool {
1970+
match v {
1971+
Some(s) => {
1972+
let slot = slot.get_or_insert_default();
1973+
for opt in s.split(',') {
1974+
match opt {
1975+
"global" => slot.mode = Some(StackProtectorGuardMode::Global),
1976+
"tls" => slot.mode = Some(StackProtectorGuardMode::Tls),
1977+
"sysreg" => slot.mode = Some(StackProtectorGuardMode::Sysreg),
1978+
s if let Some(value) = s.strip_prefix("offset=") => {
1979+
match value.parse::<u32>() {
1980+
Ok(n) => slot.offset = Some(n),
1981+
Err(_) => return false,
1982+
}
1983+
}
1984+
s if let Some(value) = s.strip_prefix("reg=") => {
1985+
if value.is_empty() {
1986+
return false;
1987+
}
1988+
slot.reg = Some(value.to_string());
1989+
}
1990+
s if let Some(value) = s.strip_prefix("symbol=") => {
1991+
if value.is_empty() {
1992+
return false;
1993+
}
1994+
slot.symbol = Some(value.to_string());
1995+
}
1996+
_ => return false,
1997+
}
1998+
}
1999+
}
2000+
_ => return false,
2001+
}
2002+
true
2003+
}
2004+
19642005
pub(crate) fn parse_collapse_macro_debuginfo(
19652006
slot: &mut CollapseMacroDebuginfo,
19662007
v: Option<&str>,
@@ -2757,6 +2798,8 @@ written to standard error output)"),
27572798
#[rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field")]
27582799
stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED] { MITIGATION: StackProtector },
27592800
"control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
2801+
stack_protector_guard: Option<StackProtectorGuard> = (None, parse_stack_protector_guard, [TRACKED] { TARGET_MODIFIER: StackProtectorGuard },
2802+
"stack protector guard settings (`mode[,offset=N][,reg=R][,symbol=S]`; modes: `global`, `tls`, `sysreg`)"),
27602803
staticlib_allow_rdylib_deps: bool = (false, parse_bool, [TRACKED],
27612804
"allow staticlibs to have rust dylib dependencies"),
27622805
staticlib_hide_internal_symbols: bool = (false, parse_bool, [TRACKED],

compiler/rustc_session/src/session.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,57 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
13071307
}
13081308
}
13091309

1310+
if let Some(ref guard) = sess.opts.unstable_opts.stack_protector_guard {
1311+
if let Some(ref mode) = guard.mode {
1312+
let valid: Option<(&[config::StackProtectorGuardMode], &str)> = match sess.target.arch {
1313+
Arch::X86 | Arch::X86_64 => Some((
1314+
&[
1315+
config::StackProtectorGuardMode::Tls,
1316+
config::StackProtectorGuardMode::Global,
1317+
],
1318+
"tls, global",
1319+
)),
1320+
Arch::AArch64 => Some((
1321+
&[
1322+
config::StackProtectorGuardMode::Sysreg,
1323+
config::StackProtectorGuardMode::Global,
1324+
],
1325+
"sysreg, global",
1326+
)),
1327+
Arch::Arm => Some((
1328+
&[
1329+
config::StackProtectorGuardMode::Tls,
1330+
config::StackProtectorGuardMode::Global,
1331+
],
1332+
"tls, global",
1333+
)),
1334+
Arch::RiscV32 | Arch::RiscV64 => Some((
1335+
&[
1336+
config::StackProtectorGuardMode::Sysreg,
1337+
config::StackProtectorGuardMode::Global,
1338+
],
1339+
"sysreg, global",
1340+
)),
1341+
_ => None,
1342+
};
1343+
match valid {
1344+
Some((modes, _)) if modes.contains(mode) => {}
1345+
Some((_, valid_str)) => {
1346+
sess.dcx().emit_err(errors::StackProtectorGuardInvalidValue {
1347+
arch: sess.target.arch.to_string(),
1348+
guard: mode.as_str().to_string(),
1349+
valid: valid_str.to_string(),
1350+
});
1351+
}
1352+
None => {
1353+
sess.dcx().emit_err(errors::StackProtectorGuardUnsupportedArch {
1354+
arch: sess.target.arch.to_string(),
1355+
});
1356+
}
1357+
}
1358+
}
1359+
}
1360+
13101361
if sess.opts.unstable_opts.small_data_threshold.is_some() {
13111362
if sess.target.small_data_threshold_support() == SmallDataThresholdSupport::None {
13121363
sess.dcx().emit_warn(errors::SmallDataThresholdNotSupportedForTarget {

0 commit comments

Comments
 (0)