diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 4e8c8aaaf5c8a..53ca2f3f6c2f1 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -94,7 +94,9 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec { - if let Err(reason) = stability.toggle_allowed() { + if let Err(reason) = + stability.toggle_allowed(|flag| sess.opts.target_feature_flag_enabled(flag)) + { sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, enabled: if enable { "enabled" } else { "disabled" }, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 53611c746a722..222fe4b4601a0 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -728,7 +728,9 @@ pub(crate) fn global_llvm_features( sess.dcx().emit_warn(unknown_feature); } Some((_, stability, _)) => { - if let Err(reason) = stability.toggle_allowed() { + if let Err(reason) = stability + .toggle_allowed(|flag| sess.opts.target_feature_flag_enabled(flag)) + { sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, enabled: if enable { "enabled" } else { "disabled" }, diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index d8b9bdb55da69..673785cc2230a 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -64,7 +64,9 @@ pub(crate) fn from_target_feature_attr( // Only allow target features whose feature gates have been enabled // and which are permitted to be toggled. - if let Err(reason) = stability.toggle_allowed() { + if let Err(reason) = + stability.toggle_allowed(|flag| tcx.sess.opts.target_feature_flag_enabled(flag)) + { tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr { span: item.span(), feature, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 2fb4b27b88905..1d1fe6a4cf4d9 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2528,6 +2528,8 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches); + Options::fill_target_features_by_flags(&unstable_opts, &mut cg); + let cg = cg; let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m)); @@ -2942,11 +2944,11 @@ pub(crate) mod dep_tracking { use super::{ AutoDiff, BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, - InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, - LtoCli, MirStripDebugInfo, NextSolverConfig, OomStrategy, OptLevel, OutFileName, - OutputType, OutputTypes, PatchableFunctionEntry, Polonius, RemapPathScopeComponents, - ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, - SymbolManglingVersion, WasiExecModel, + HardenSls, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, + LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, OomStrategy, OptLevel, + OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, + RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, + SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3047,6 +3049,7 @@ pub(crate) mod dep_tracking { Polonius, InliningThreshold, FunctionReturn, + HardenSls, WasmCAbi, Align, ); @@ -3302,6 +3305,16 @@ pub enum FunctionReturn { ThunkExtern, } +/// The different settings that the `-Zharden-sls` flag can have. +#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] +pub enum HardenSls { + #[default] + None, + All, + Return, + IndirectJmp, +} + /// Whether extra span comments are included when dumping MIR, via the `-Z mir-include-spans` flag. /// By default, only enabled in the NLL MIR dumps, and disabled in all other passes. #[derive(Clone, Copy, Default, PartialEq, Debug)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 35819f896c5bd..e3cb44c9e058c 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -286,6 +286,40 @@ macro_rules! top_level_options { mods.sort_by(|a, b| a.opt.cmp(&b.opt)); mods } + + pub fn target_feature_flag_enabled(&self, flag: &str) -> bool { + match flag { + "x86-retpoline" => self.unstable_opts.x86_retpoline, + "harden-sls" => !matches!(self.unstable_opts.harden_sls, HardenSls::None), + _ => false, + } + } + + pub fn fill_target_features_by_flags( + unstable_opts: &UnstableOptions, cg: &mut CodegenOptions + ) { + if unstable_opts.x86_retpoline { + if !cg.target_feature.is_empty() { + cg.target_feature.push(','); + } + cg.target_feature.push_str( + "+retpoline-external-thunk,\ + +retpoline-indirect-branches,\ + +retpoline-indirect-calls" + ); + } + if let Some(features) = match unstable_opts.harden_sls { + HardenSls::None => None, + HardenSls::All => Some("+harden-sls-ijmp,+harden-sls-ret"), + HardenSls::Return => Some("+harden-sls-ret"), + HardenSls::IndirectJmp => Some("+harden-sls-ijmp"), + } { + if !cg.target_feature.is_empty() { + cg.target_feature.push(','); + } + cg.target_feature.push_str(features); + } + } } ); } @@ -790,6 +824,7 @@ mod desc { "either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number"; pub(crate) const parse_llvm_module_flag: &str = ":::. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)"; pub(crate) const parse_function_return: &str = "`keep` or `thunk-extern`"; + pub(crate) const parse_harden_sls: &str = "`none`, `all`, `return` or `indirect-jmp`"; pub(crate) const parse_wasm_c_abi: &str = "`legacy` or `spec`"; pub(crate) const parse_mir_include_spans: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)"; @@ -1886,6 +1921,17 @@ pub mod parse { true } + pub(crate) fn parse_harden_sls(slot: &mut HardenSls, v: Option<&str>) -> bool { + match v { + Some("none") => *slot = HardenSls::None, + Some("all") => *slot = HardenSls::All, + Some("return") => *slot = HardenSls::Return, + Some("indirect-jmp") => *slot = HardenSls::IndirectJmp, + _ => return false, + } + true + } + pub(crate) fn parse_wasm_c_abi(slot: &mut WasmCAbi, v: Option<&str>) -> bool { match v { Some("spec") => *slot = WasmCAbi::Spec, @@ -2216,6 +2262,9 @@ options! { graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED], "use the given `fontname` in graphviz output; can be overridden by setting \ environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)"), + harden_sls: HardenSls = (HardenSls::None, parse_harden_sls, [TRACKED TARGET_MODIFIER], + "flag to mitigate against straight line speculation (SLS) [none|all|return|indirect-jmp] \ + (default: none)"), has_thread_local: Option = (None, parse_opt_bool, [TRACKED], "explicitly enable the `cfg(target_thread_local)` directive"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], @@ -2608,6 +2657,9 @@ written to standard error output)"), "use spec-compliant C ABI for `wasm32-unknown-unknown` (default: legacy)"), write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED], "whether long type names should be written to files instead of being printed in errors"), + x86_retpoline: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER], + "enable retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \ + target features (default: no)"), // tidy-alphabetical-end // If you add a new option, please update: diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index eb2417e0a20a3..c3e857c924a07 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -34,6 +34,9 @@ pub enum Stability { /// particular for features are actually ABI configuration flags (not all targets are as nice as /// RISC-V and have an explicit way to set the ABI separate from target features). Forbidden { reason: &'static str }, + /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set + /// by target modifier flag. Target modifier flags are tracked to be consistent in linked modules. + EnabledByTargetModifierFlag { reason: &'static str, flag: &'static str }, } use Stability::*; @@ -49,6 +52,7 @@ impl HashStable for Stability { Stability::Forbidden { reason } => { reason.hash_stable(hcx, hasher); } + Stability::EnabledByTargetModifierFlag { .. } => {} } } } @@ -74,15 +78,23 @@ impl Stability { Stability::Unstable(nightly_feature) => Some(nightly_feature), Stability::Stable { .. } => None, Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"), + Stability::EnabledByTargetModifierFlag { .. } => None, } } /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. /// (It might still be nightly-only even if this returns `true`, so make sure to also check /// `requires_nightly`.) - pub fn toggle_allowed(&self) -> Result<(), &'static str> { + pub fn toggle_allowed(&self, flag_enabled: impl Fn(&str) -> bool) -> Result<(), &'static str> { match self { Stability::Forbidden { reason } => Err(reason), + Stability::EnabledByTargetModifierFlag { reason, flag } => { + if !flag_enabled(*flag) { + Err(reason) + } else { + Ok(()) + } + } _ => Ok(()), } } @@ -409,6 +421,22 @@ const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("fma", Stable, &["avx"]), ("fxsr", Stable, &[]), ("gfni", Unstable(sym::avx512_target_feature), &["sse2"]), + ( + "harden-sls-ijmp", + Stability::EnabledByTargetModifierFlag { + reason: "use `harden-sls` target modifier flag instead", + flag: "harden-sls", + }, + &[], + ), + ( + "harden-sls-ret", + Stability::EnabledByTargetModifierFlag { + reason: "use `harden-sls` target modifier flag instead", + flag: "harden-sls", + }, + &[], + ), ("kl", Unstable(sym::keylocker_x86), &["sse2"]), ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), ("lzcnt", Stable, &[]), @@ -418,6 +446,30 @@ const X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("prfchw", Unstable(sym::prfchw_target_feature), &[]), ("rdrand", Stable, &[]), ("rdseed", Stable, &[]), + ( + "retpoline-external-thunk", + Stability::EnabledByTargetModifierFlag { + reason: "use `x86-retpoline` target modifier flag instead", + flag: "x86-retpoline", + }, + &[], + ), + ( + "retpoline-indirect-branches", + Stability::EnabledByTargetModifierFlag { + reason: "use `x86-retpoline` target modifier flag instead", + flag: "x86-retpoline", + }, + &[], + ), + ( + "retpoline-indirect-calls", + Stability::EnabledByTargetModifierFlag { + reason: "use `x86-retpoline` target modifier flag instead", + flag: "x86-retpoline", + }, + &[], + ), ("rtm", Unstable(sym::rtm_target_feature), &[]), ("sha", Stable, &["sse2"]), ("sha512", Unstable(sym::sha512_sm_x86), &["avx2"]), diff --git a/tests/codegen/harden-sls.rs b/tests/codegen/harden-sls.rs new file mode 100644 index 0000000000000..049bbf91e12bc --- /dev/null +++ b/tests/codegen/harden-sls.rs @@ -0,0 +1,34 @@ +// ignore-tidy-linelength +// Test that the `harden-sls-ijmp`, `harden-sls-ret` target features is (not) emitted when +// the `harden-sls=[none|all|return|indirect-jmp]` flag is (not) set. + +//@ revisions: none all return indirect_jmp +//@ needs-llvm-components: x86 +//@ compile-flags: --target x86_64-unknown-linux-gnu +//@ [none] compile-flags: -Zharden-sls=none +//@ [all] compile-flags: -Zharden-sls=all +//@ [return] compile-flags: -Zharden-sls=return +//@ [indirect_jmp] compile-flags: -Zharden-sls=indirect-jmp + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[no_mangle] +pub fn foo() { + // CHECK: @foo() unnamed_addr #0 + + // none-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+harden-sls-ijmp{{.*}} } + // none-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+harden-sls-ret{{.*}} } + + // all: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+harden-sls-ijmp,+harden-sls-ret{{.*}} } + + // return-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+harden-sls-ijmp{{.*}} } + // return: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+harden-sls-ret{{.*}} } + + // indirect_jmp-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+harden-sls-ret{{.*}} } + // indirect_jmp: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+harden-sls-ijmp{{.*}} } +} diff --git a/tests/codegen/retpoline.rs b/tests/codegen/retpoline.rs new file mode 100644 index 0000000000000..0361008e91e55 --- /dev/null +++ b/tests/codegen/retpoline.rs @@ -0,0 +1,27 @@ +// ignore-tidy-linelength +// Test that the +// `retpoline-external-thunk`, `retpoline-indirect-branches`, `retpoline-indirect-calls` +// target features are (not) emitted when the `x86-retpoline` flag is (not) set. + +//@ revisions: disabled enabled +//@ needs-llvm-components: x86 +//@ compile-flags: --target x86_64-unknown-linux-gnu +//@ [enabled] compile-flags: -Zx86-retpoline + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +#[lang = "sized"] +trait Sized {} + +#[no_mangle] +pub fn foo() { + // CHECK: @foo() unnamed_addr #0 + + // disabled-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-external-thunk{{.*}} } + // disabled-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-indirect-branches{{.*}} } + // disabled-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-indirect-calls{{.*}} } + + // enabled: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-external-thunk,+retpoline-indirect-branches,+retpoline-indirect-calls{{.*}} } +} diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 70852423bdbef..4ecb16c0f52f3 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -112,6 +112,8 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `hard-float` `hard-float-abi` `hard-tp` +`harden-sls-ijmp` +`harden-sls-ret` `hbc` `high-registers` `hvx` @@ -182,6 +184,9 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `relax` `relaxed-simd` `reserve-x18` +`retpoline-external-thunk` +`retpoline-indirect-branches` +`retpoline-indirect-calls` `rtm` `sb` `sha` diff --git a/tests/ui/target-feature/harden-sls-target-feature-flag.by_feature.stderr b/tests/ui/target-feature/harden-sls-target-feature-flag.by_feature.stderr new file mode 100644 index 0000000000000..68a0ecc686d78 --- /dev/null +++ b/tests/ui/target-feature/harden-sls-target-feature-flag.by_feature.stderr @@ -0,0 +1,12 @@ +warning: target feature `harden-sls-ijmp` cannot be enabled with `-Ctarget-feature`: use `harden-sls` target modifier flag instead + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: target feature `harden-sls-ret` cannot be enabled with `-Ctarget-feature`: use `harden-sls` target modifier flag instead + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: 2 warnings emitted + diff --git a/tests/ui/target-feature/harden-sls-target-feature-flag.rs b/tests/ui/target-feature/harden-sls-target-feature-flag.rs new file mode 100644 index 0000000000000..54405c46fda07 --- /dev/null +++ b/tests/ui/target-feature/harden-sls-target-feature-flag.rs @@ -0,0 +1,14 @@ +//@ revisions: by_flag by_feature +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ [by_flag]compile-flags: -Zharden-sls=all +//@ [by_feature]compile-flags: -Ctarget-feature=+harden-sls-ijmp,+harden-sls-ret +//@ [by_flag]build-pass +// For now this is just a warning. +//@ [by_feature]build-pass +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr new file mode 100644 index 0000000000000..e2b6078b7a8c9 --- /dev/null +++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr @@ -0,0 +1,7 @@ +warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `x86-retpoline` target modifier flag instead + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + +warning: 1 warning emitted + diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.rs b/tests/ui/target-feature/retpoline-target-feature-flag.rs new file mode 100644 index 0000000000000..76a0536f607fd --- /dev/null +++ b/tests/ui/target-feature/retpoline-target-feature-flag.rs @@ -0,0 +1,14 @@ +//@ revisions: by_flag by_feature +//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib +//@ needs-llvm-components: x86 +//@ [by_flag]compile-flags: -Zx86-retpoline +//@ [by_feature]compile-flags: -Ctarget-feature=+retpoline-external-thunk +//@ [by_flag]build-pass +// For now this is just a warning. +//@ [by_feature]build-pass +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] + +#[lang = "sized"] +pub trait Sized {}