diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -2629,6 +2629,11 @@ Enable only control-flow mitigations for Load Value Injection (LVI) +.. option:: -mseses, -mno-seses + +Enable speculative execution side effect suppression along with the LVI control- +flow mitigations. + .. option:: -mmacosx-version-min=, -mmacos-version-min= Set Mac OS X deployment target 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 @@ -2342,6 +2342,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 @@ -435,6 +435,13 @@ "ourselves. Only has effect when combined with some other retpoline " "feature", [FeatureRetpolineIndirectCalls]>; +// 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.">; // Mitigate LVI attacks against indirect calls/branches and call returns def FeatureLVIControlFlowIntegrity : 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 @@ -86,13 +86,14 @@ bool X86SpeculativeExecutionSideEffectSuppression::runOnMachineFunction( MachineFunction &MF) { - if (!EnableSpeculativeExecutionSideEffectSuppression) + const X86Subtarget &Subtarget = MF.getSubtarget(); + if (!Subtarget.useSpeculativeExecutionSideEffectSuppression() && + !EnableSpeculativeExecutionSideEffectSuppression) return false; LLVM_DEBUG(dbgs() << "********** " << getPassName() << " : " << MF.getName() << " **********\n"); bool Modified = false; - const X86Subtarget &Subtarget = MF.getSubtarget(); const X86InstrInfo *TII = Subtarget.getInstrInfo(); for (MachineBasicBlock &MBB : MF) { MachineInstr *FirstTerminator = nullptr; 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 @@ -437,6 +437,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; @@ -744,6 +747,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; }