diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -2755,6 +2755,10 @@ Make StdCall calling convention the default +.. option:: -mseses, -mno-seses + +Enable speculative execution side effect suppression (SESES). Includes LVI control flow integrity mitigations + .. option:: -msign-return-address= Select return address signing scope diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2264,6 +2264,11 @@ HelpText<"Enable only control-flow mitigations for Load Value Injection (LVI)">; def mno_lvi_cfi : Flag<["-"], "mno-lvi-cfi">, Group, Flags<[CoreOption,DriverOption]>, HelpText<"Disable control-flow mitigations for Load Value Injection (LVI)">; +def m_seses : Flag<["-"], "mseses">, Group, Flags<[CoreOption, DriverOption]>, + HelpText<"Enable speculative execution side effect suppression (SESES). " + "Includes LVI control flow integrity mitigations">; +def mno_seses : Flag<["-"], "mno-seses">, Group, Flags<[CoreOption, DriverOption]>, + HelpText<"Disable speculative execution side effect suppression (SESES)">; def mrelax : Flag<["-"], "mrelax">, Group, HelpText<"Enable linker relaxation">; diff --git a/clang/lib/Driver/ToolChains/Arch/X86.cpp b/clang/lib/Driver/ToolChains/Arch/X86.cpp --- a/clang/lib/Driver/ToolChains/Arch/X86.cpp +++ b/clang/lib/Driver/ToolChains/Arch/X86.cpp @@ -184,6 +184,24 @@ LVIOpt = options::OPT_mlvi_cfi; } + if (Args.hasFlag(options::OPT_m_seses, options::OPT_mno_seses, false)) { + if (LVIOpt == options::OPT_mlvi_hardening) + D.Diag(diag::err_drv_argument_not_allowed_with) + << D.getOpts().getOptionName(options::OPT_mlvi_hardening) + << D.getOpts().getOptionName(options::OPT_m_seses); + + if (SpectreOpt != clang::driver::options::ID::OPT_INVALID) + D.Diag(diag::err_drv_argument_not_allowed_with) + << D.getOpts().getOptionName(SpectreOpt) + << D.getOpts().getOptionName(options::OPT_m_seses); + + Features.push_back("+seses"); + if (!Args.hasArg(options::OPT_mno_lvi_cfi)) { + Features.push_back("+lvi-cfi"); + LVIOpt = options::OPT_mlvi_cfi; + } + } + if (SpectreOpt != clang::driver::options::ID::OPT_INVALID && LVIOpt != clang::driver::options::ID::OPT_INVALID) { D.Diag(diag::err_drv_argument_not_allowed_with) diff --git a/clang/test/Driver/x86-target-features.c b/clang/test/Driver/x86-target-features.c --- a/clang/test/Driver/x86-target-features.c +++ b/clang/test/Driver/x86-target-features.c @@ -178,6 +178,27 @@ // RUN: %clang -target i386-linux-gnu -mlvi-hardening -mretpoline-external-thunk %s -### -o %t.o 2>&1 | FileCheck -check-prefix=LVIHARDENING-RETPOLINE-EXTERNAL-THUNK %s // LVIHARDENING-RETPOLINE-EXTERNAL-THUNK: error: invalid argument 'mretpoline-external-thunk' not allowed with 'mlvi-hardening' +// RUN: %clang -target i386-linux-gnu -mseses %s -### -o %t.o 2>&1 | FileCheck -check-prefix=SESES %s +// RUN: %clang -target i386-linux-gnu -mno-seses %s -### -o %t.o 2>&1 | FileCheck -check-prefix=NO-SESES %s +// SESES: "-target-feature" "+seses" +// SESES: "-target-feature" "+lvi-cfi" +// NO-SESES-NOT: seses +// NO-SESES-NOT: lvi-cfi + +// RUN: %clang -target i386-linux-gnu -mseses -mno-lvi-cfi %s -### -o %t.o 2>&1 | FileCheck -check-prefix=SESES-NOLVICFI %s +// SESES-NOLVICFI: "-target-feature" "+seses" +// SESES-NOLVICFI-NOT: lvi-cfi + +// RUN: %clang -target i386-linux-gnu -mseses -mspeculative-load-hardening %s -### -o %t.o 2>&1 | FileCheck -check-prefix=SESES-SLH %s +// SESES-SLH: error: invalid argument 'mspeculative-load-hardening' not allowed with 'mseses' +// RUN: %clang -target i386-linux-gnu -mseses -mretpoline %s -### -o %t.o 2>&1 | FileCheck -check-prefix=SESES-RETPOLINE %s +// SESES-RETPOLINE: error: invalid argument 'mretpoline' not allowed with 'mseses' +// RUN: %clang -target i386-linux-gnu -mseses -mretpoline-external-thunk %s -### -o %t.o 2>&1 | FileCheck -check-prefix=SESES-RETPOLINE-EXTERNAL-THUNK %s +// SESES-RETPOLINE-EXTERNAL-THUNK: error: invalid argument 'mretpoline-external-thunk' not allowed with 'mseses' + +// RUN: %clang -target i386-linux-gnu -mseses -mlvi-hardening %s -### -o %t.o 2>&1 | FileCheck -check-prefix=SESES-LVIHARDENING %s +// SESES-LVIHARDENING: error: invalid argument 'mlvi-hardening' not allowed with 'mseses' + // RUN: %clang -target i386-linux-gnu -mwaitpkg %s -### -o %t.o 2>&1 | FileCheck -check-prefix=WAITPKG %s // RUN: %clang -target i386-linux-gnu -mno-waitpkg %s -### -o %t.o 2>&1 | FileCheck -check-prefix=NO-WAITPKG %s // WAITPKG: "-target-feature" "+waitpkg" diff --git a/llvm/lib/Target/X86/X86.td b/llvm/lib/Target/X86/X86.td --- a/llvm/lib/Target/X86/X86.td +++ b/llvm/lib/Target/X86/X86.td @@ -455,6 +455,15 @@ "LFENCE instruction to serialize control flow. Also decompose RET " "instructions into a POP+LFENCE+JMP sequence.">; +// Enable SESES to mitigate speculative execution attacks +def FeatureSpeculativeExecutionSideEffectSuppression + : SubtargetFeature< + "seses", "UseSpeculativeExecutionSideEffectSuppression", "true", + "Prevent speculative execution side channel timing attacks by " + "inserting a speculation barrier before memory reads, memory writes, " + "and conditional branches. Implies LVI Control Flow integrity.", + [FeatureLVIControlFlowIntegrity]>; + // Mitigate LVI attacks against data loads def FeatureLVILoadHardening : SubtargetFeature< diff --git a/llvm/lib/Target/X86/X86SpeculativeExecutionSideEffectSuppression.cpp b/llvm/lib/Target/X86/X86SpeculativeExecutionSideEffectSuppression.cpp --- a/llvm/lib/Target/X86/X86SpeculativeExecutionSideEffectSuppression.cpp +++ b/llvm/lib/Target/X86/X86SpeculativeExecutionSideEffectSuppression.cpp @@ -30,7 +30,7 @@ STATISTIC(NumLFENCEsInserted, "Number of lfence instructions inserted"); static cl::opt EnableSpeculativeExecutionSideEffectSuppression( - "x86-seses-enable", + "x86-seses-enable-without-lvi-cfi", cl::desc("Force enable speculative execution side effect suppression. " "(Note: User must pass -mlvi-cfi in order to mitigate indirect " "branches and returns.)"), @@ -91,10 +91,12 @@ const auto &OptLevel = MF.getTarget().getOptLevel(); const X86Subtarget &Subtarget = MF.getSubtarget(); - // Check whether SESES needs to run as the fallback for LVI at O0 or if the - // user explicitly passed the SESES flag. + // Check whether SESES needs to run as the fallback for LVI at O0, whether the + // user explicitly passed an SESES flag, or whether the SESES target feature + // was set. if (!EnableSpeculativeExecutionSideEffectSuppression && - !(Subtarget.useLVILoadHardening() && OptLevel == CodeGenOpt::None)) + !(Subtarget.useLVILoadHardening() && OptLevel == CodeGenOpt::None) && + !Subtarget.useSpeculativeExecutionSideEffectSuppression()) return false; LLVM_DEBUG(dbgs() << "********** " << getPassName() << " : " << MF.getName() diff --git a/llvm/lib/Target/X86/X86Subtarget.h b/llvm/lib/Target/X86/X86Subtarget.h --- a/llvm/lib/Target/X86/X86Subtarget.h +++ b/llvm/lib/Target/X86/X86Subtarget.h @@ -442,6 +442,9 @@ /// POP+LFENCE+JMP sequence. bool UseLVIControlFlowIntegrity = false; + /// Enable Speculative Execution Side Effect Suppression + bool UseSpeculativeExecutionSideEffectSuppression = false; + /// Insert LFENCE instructions to prevent data speculatively injected into /// loads from being used maliciously. bool UseLVILoadHardening = false; @@ -759,6 +762,9 @@ bool useGLMDivSqrtCosts() const { return UseGLMDivSqrtCosts; } bool useLVIControlFlowIntegrity() const { return UseLVIControlFlowIntegrity; } bool useLVILoadHardening() const { return UseLVILoadHardening; } + bool useSpeculativeExecutionSideEffectSuppression() const { + return UseSpeculativeExecutionSideEffectSuppression; + } unsigned getPreferVectorWidth() const { return PreferVectorWidth; } unsigned getRequiredVectorWidth() const { return RequiredVectorWidth; } diff --git a/llvm/test/CodeGen/X86/speculative-execution-side-effect-suppression.ll b/llvm/test/CodeGen/X86/speculative-execution-side-effect-suppression.ll --- a/llvm/test/CodeGen/X86/speculative-execution-side-effect-suppression.ll +++ b/llvm/test/CodeGen/X86/speculative-execution-side-effect-suppression.ll @@ -1,8 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc -mtriple=x86_64-unknown-linux-gnu -x86-seses-enable %s -o - | FileCheck %s -; RUN: llc -mtriple=x86_64-unknown-linux-gnu -x86-seses-enable -x86-seses-one-lfence-per-bb %s -o - | FileCheck %s --check-prefix=X86-ONE-LFENCE -; RUN: llc -mtriple=x86_64-unknown-linux-gnu -x86-seses-enable -x86-seses-omit-branch-lfences %s -o - | FileCheck %s --check-prefix=X86-OMIT-BR -; RUN: llc -mtriple=x86_64-unknown-linux-gnu -x86-seses-enable -x86-seses-only-lfence-non-const %s -o - | FileCheck %s --check-prefix=X86-NON-CONST +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -x86-seses-enable-without-lvi-cfi %s -o - | FileCheck %s +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -x86-seses-enable-without-lvi-cfi -x86-seses-one-lfence-per-bb %s -o - | FileCheck %s --check-prefix=X86-ONE-LFENCE +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -x86-seses-enable-without-lvi-cfi -x86-seses-omit-branch-lfences %s -o - | FileCheck %s --check-prefix=X86-OMIT-BR +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -x86-seses-enable-without-lvi-cfi -x86-seses-only-lfence-non-const %s -o - | FileCheck %s --check-prefix=X86-NON-CONST define void @_Z4buzzv() { ; CHECK-LABEL: _Z4buzzv: