From a3a0c0f3929511a930bca36d37030fb80eddb4c4 Mon Sep 17 00:00:00 2001 From: Andrew Zhogin Date: Sun, 2 Feb 2025 20:25:00 +0700 Subject: [PATCH] -Zharden-sls flag (target modifier) added to enable mitigation against straight line speculation (SLS) --- compiler/rustc_codegen_gcc/src/gcc_util.rs | 1 + compiler/rustc_codegen_llvm/src/llvm_util.rs | 1 + .../rustc_codegen_ssa/src/target_features.rs | 13 ++++ compiler/rustc_session/src/config.rs | 21 +++-- compiler/rustc_session/src/options.rs | 15 ++++ compiler/rustc_target/src/target_features.rs | 10 +++ tests/assembly-llvm/x86_64-sls.rs | 76 +++++++++++++++++++ tests/codegen-llvm/harden-sls.rs | 35 +++++++++ ...-sls-target-feature-flag.by_feature.stderr | 12 +++ .../harden-sls-target-feature-flag.rs | 15 ++++ 10 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 tests/assembly-llvm/x86_64-sls.rs create mode 100644 tests/codegen-llvm/harden-sls.rs create mode 100644 tests/ui/target-feature/harden-sls-target-feature-flag.by_feature.stderr create mode 100644 tests/ui/target-feature/harden-sls-target-feature-flag.rs diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 42ba40692b75c..cc82a4c9e8e74 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -6,6 +6,7 @@ use smallvec::{SmallVec, smallvec}; fn gcc_features_by_flags(sess: &Session, features: &mut Vec) { target_features::retpoline_features_by_flags(sess, features); + target_features::sls_features_by_flags(sess, features); // FIXME: LLVM also sets +reserve-x18 here under some conditions. } diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 90f7cd43268fa..072dac2f9ddcc 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -647,6 +647,7 @@ pub(crate) fn target_cpu(sess: &Session) -> &str { /// The target features for compiler flags other than `-Ctarget-features`. fn llvm_features_by_flags(sess: &Session, features: &mut Vec) { target_features::retpoline_features_by_flags(sess, features); + target_features::sls_features_by_flags(sess, features); // -Zfixed-x18 if sess.opts.unstable_opts.fixed_x18 { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index b5aa50f485125..78fd51ce5614c 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -7,6 +7,7 @@ use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::Session; +use rustc_session::config::HardenSls; use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; @@ -412,6 +413,18 @@ pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec) { } } +pub fn sls_features_by_flags(sess: &Session, features: &mut Vec) { + match &sess.opts.unstable_opts.harden_sls { + HardenSls::None => (), + HardenSls::All => { + features.push("+harden-sls-ijmp".into()); + features.push("+harden-sls-ret".into()); + } + HardenSls::Return => features.push("+harden-sls-ret".into()), + HardenSls::IndirectJmp => features.push("+harden-sls-ijmp".into()), + } +} + pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { rust_target_features: |tcx, cnum| { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 9793d8091e2ab..2087464797b6a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3213,11 +3213,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, Offload, OomStrategy, OptLevel, OutFileName, - OutputType, OutputTypes, PatchableFunctionEntry, Polonius, RemapPathScopeComponents, - ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, - SymbolManglingVersion, WasiExecModel, + HardenSls, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, + LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OomStrategy, + OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, + RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, + SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3320,6 +3320,7 @@ pub(crate) mod dep_tracking { Polonius, InliningThreshold, FunctionReturn, + HardenSls, Align, ); @@ -3574,6 +3575,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 6d5be2d92cd2a..0235cc74681f1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -808,6 +808,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 = "`spec`"; pub(crate) const parse_mir_include_spans: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or `nll` (default: `nll`)"; @@ -1936,6 +1937,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 (), v: Option<&str>) -> bool { v == Some("spec") } @@ -2273,6 +2285,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"), higher_ranked_assumptions: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index e45300b59cc73..f04bd3c66ba35 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -437,6 +437,16 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("fma", Stable, &["avx"]), ("fxsr", Stable, &[]), ("gfni", Stable, &["sse2"]), + ( + "harden-sls-ijmp", + Stability::Forbidden { reason: "use `harden-sls` compiler flag instead" }, + &[], + ), + ( + "harden-sls-ret", + Stability::Forbidden { reason: "use `harden-sls` compiler flag instead" }, + &[], + ), ("kl", Stable, &["sse2"]), ("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]), ("lzcnt", Stable, &[]), diff --git a/tests/assembly-llvm/x86_64-sls.rs b/tests/assembly-llvm/x86_64-sls.rs new file mode 100644 index 0000000000000..2a24e2116aa9a --- /dev/null +++ b/tests/assembly-llvm/x86_64-sls.rs @@ -0,0 +1,76 @@ +// Test harden-sls flag + +#![feature(core_intrinsics)] +//@ revisions: NONE ALL RET IJMP +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 -Cunsafe-allow-abi-mismatch=harden-sls +//@ [NONE] compile-flags: -Zharden-sls=none +//@ [ALL] compile-flags: -Zharden-sls=all +//@ [RET] compile-flags: -Zharden-sls=return +//@ [IJMP] compile-flags: -Zharden-sls=indirect-jmp +//@ only-x86_64 +#![crate_type = "lib"] + +#[no_mangle] +pub fn double_return(a: i32, b: i32) -> i32 { + // CHECK-LABEL: double_return: + // CHECK: jle + // CHECK-NOT: int3 + // CHECK: retq + // RET-NEXT: int3 + // ALL-NEXT: int3 + // IJMP-NOT: int3 + // NONE-NOT: int3 + // CHECK: retq + // RET-NEXT: int3 + // ALL-NEXT: int3 + // IJMP-NOT: int3 + // NONE-NOT: int3 + if a > 0 { + unsafe { std::intrinsics::unchecked_div(a, b) } + } else { + unsafe { std::intrinsics::unchecked_div(b, a) } + } +} + +#[no_mangle] +pub fn indirect_branch(a: i32, b: i32, i: i32) -> i32 { + // CHECK-LABEL: indirect_branch: + // CHECK: jmpq * + // RET-NOT: int3 + // NONE-NOT: int3 + // IJMP-NEXT: int3 + // ALL-NEXT: int3 + // CHECK: retq + // RET-NEXT: int3 + // ALL-NEXT: int3 + // IJMP-NOT: int3 + // NONE-NOT: int3 + // CHECK: retq + // RET-NEXT: int3 + // ALL-NEXT: int3 + // IJMP-NOT: int3 + // NONE-NOT: int3 + match i { + 0 => unsafe { std::intrinsics::unchecked_div(a, b) }, + 1 => unsafe { std::intrinsics::unchecked_div(b, a) }, + 2 => unsafe { std::intrinsics::unchecked_div(b, a) + 2 }, + 3 => unsafe { std::intrinsics::unchecked_div(b, a) + 3 }, + 4 => unsafe { std::intrinsics::unchecked_div(b, a) + 4 }, + 5 => unsafe { std::intrinsics::unchecked_div(b, a) + 5 }, + 6 => unsafe { std::intrinsics::unchecked_div(b, a) + 6 }, + _ => panic!(""), + } +} + +#[no_mangle] +pub fn bar(ptr: fn()) { + // CHECK-LABEL: bar: + // CHECK: jmpq * + // RET-NOT: int3 + // NONE-NOT: int3 + // IJMP-NEXT: int3 + // ALL-NEXT: int3 + // CHECK-NOT: ret + ptr() +} diff --git a/tests/codegen-llvm/harden-sls.rs b/tests/codegen-llvm/harden-sls.rs new file mode 100644 index 0000000000000..76ca2ab702af5 --- /dev/null +++ b/tests/codegen-llvm/harden-sls.rs @@ -0,0 +1,35 @@ +// 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. + +//@ add-core-stubs +//@ 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)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[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/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..71a23f8fc6af8 --- /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` compiler 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` compiler 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..d8f855317323f --- /dev/null +++ b/tests/ui/target-feature/harden-sls-target-feature-flag.rs @@ -0,0 +1,15 @@ +//@ add-core-stubs +//@ 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] + +//[by_feature]~? WARN target feature `harden-sls-ijmp` cannot be enabled with `-Ctarget-feature`: use `harden-sls` compiler flag instead +//[by_feature]~? WARN target feature `harden-sls-ret` cannot be enabled with `-Ctarget-feature`: use `harden-sls` compiler flag instead