Skip to content

Commit

Permalink
[x86/SLH] Add a real Clang flag and LLVM IR attribute for Speculative
Browse files Browse the repository at this point in the history
Load Hardening.

Wires up the existing pass to work with a proper IR attribute rather
than just a hidden/internal flag. The internal flag continues to work
for now, but I'll likely remove it soon.

Most of the churn here is adding the IR attribute. I talked about this
Kristof Beyls and he seemed at least initially OK with this direction.
The idea of using a full attribute here is that we *do* expect at least
some forms of this for other architectures. There isn't anything
*inherently* x86-specific about this technique, just that we only have
an implementation for x86 at the moment.

While we could potentially expose this as a Clang-level attribute as
well, that seems like a good question to defer for the moment as it
isn't 100% clear whether that or some other programmer interface (or
both?) would be best. We'll defer the programmer interface side of this
for now, but at least get to the point where the feature can be enabled
without relying on implementation details.

This also allows us to do something that was really hard before: we can
enable *just* the indirect call retpolines when using SLH. For x86, we
don't have any other way to mitigate indirect calls. Other architectures
may take a different approach of course, and none of this is surfaced to
user-level flags.

Differential Revision: https://reviews.llvm.org/D51157

llvm-svn: 341363
  • Loading branch information
chandlerc committed Sep 4, 2018
1 parent 9875ae4 commit 664aa86
Show file tree
Hide file tree
Showing 27 changed files with 150 additions and 26 deletions.
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Expand Up @@ -2009,6 +2009,10 @@ def mno_stackrealign : Flag<["-"], "mno-stackrealign">, Group<m_Group>;

def mretpoline : Flag<["-"], "mretpoline">, Group<m_Group>, Flags<[CoreOption,DriverOption]>;
def mno_retpoline : Flag<["-"], "mno-retpoline">, Group<m_Group>, Flags<[CoreOption,DriverOption]>;
def mspeculative_load_hardening : Flag<["-"], "mspeculative-load-hardening">,
Group<m_Group>, Flags<[CoreOption,CC1Option]>;
def mno_speculative_load_hardening : Flag<["-"], "mno-speculative-load-hardening">,
Group<m_Group>, Flags<[CoreOption]>;

def mrelax : Flag<["-"], "mrelax">, Group<m_riscv_Features_Group>,
HelpText<"Enable linker relaxation">;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Frontend/CodeGenOptions.def
Expand Up @@ -211,6 +211,7 @@ CODEGENOPT(SanitizeCoverageStackDepth, 1, 0) ///< Enable max stack depth tracing
CODEGENOPT(SanitizeStats , 1, 0) ///< Collect statistics for sanitizers.
CODEGENOPT(SimplifyLibCalls , 1, 1) ///< Set when -fbuiltin is enabled.
CODEGENOPT(SoftFloat , 1, 0) ///< -soft-float.
CODEGENOPT(SpeculativeLoadHardening, 1, 0) ///< Enable speculative load hardening.
CODEGENOPT(FineGrainedBitfieldAccesses, 1, 0) ///< Enable fine-grained bitfield accesses.
CODEGENOPT(StrictEnums , 1, 0) ///< Optimize based on strict enum definition.
CODEGENOPT(StrictVTablePointers, 1, 0) ///< Optimize based on the strict vtable pointers
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CGCall.cpp
Expand Up @@ -1784,6 +1784,9 @@ void CodeGenModule::ConstructDefaultFnAttrList(StringRef Name, bool HasOptnone,
FuncAttrs.addAttribute("stackrealign");
if (CodeGenOpts.Backchain)
FuncAttrs.addAttribute("backchain");

if (CodeGenOpts.SpeculativeLoadHardening)
FuncAttrs.addAttribute(llvm::Attribute::SpeculativeLoadHardening);
}

if (getLangOpts().assumeFunctionsAreConvergent()) {
Expand Down
18 changes: 13 additions & 5 deletions clang/lib/Driver/ToolChains/Arch/X86.cpp
Expand Up @@ -146,15 +146,23 @@ void x86::getX86TargetFeatures(const Driver &D, const llvm::Triple &Triple,

// Translate the high level `-mretpoline` flag to the specific target feature
// flags. We also detect if the user asked for retpoline external thunks but
// failed to ask for retpolines themselves. This is a bit hacky but keeps
// existing usages working. We should consider deprecated this and instead
// warning if the user requests external retpoline thunks and *doesn't*
// request some form of retpolines.
if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline)) {
// failed to ask for retpolines themselves (through any of the different
// flags). This is a bit hacky but keeps existing usages working. We should
// consider deprecating this and instead warn if the user requests external
// retpoline thunks and *doesn't* request some form of retpolines.
if (Args.hasArgNoClaim(options::OPT_mretpoline, options::OPT_mno_retpoline,
options::OPT_mspeculative_load_hardening,
options::OPT_mno_speculative_load_hardening)) {
if (Args.hasFlag(options::OPT_mretpoline, options::OPT_mno_retpoline,
false)) {
Features.push_back("+retpoline-indirect-calls");
Features.push_back("+retpoline-indirect-branches");
} else if (Args.hasFlag(options::OPT_mspeculative_load_hardening,
options::OPT_mno_speculative_load_hardening,
false)) {
// On x86, speculative load hardening relies on at least using retpolines
// for indirect calls.
Features.push_back("+retpoline-indirect-calls");
}
} else if (Args.hasFlag(options::OPT_mretpoline_external_thunk,
options::OPT_mno_retpoline_external_thunk, false)) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Expand Up @@ -4132,6 +4132,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,

Args.AddLastArg(CmdArgs, options::OPT_pthread);

Args.AddLastArg(CmdArgs, options::OPT_mspeculative_load_hardening,
options::OPT_mno_speculative_load_hardening);

RenderSSPOptions(getToolChain(), Args, CmdArgs, KernelOrKext);

// Translate -mstackrealign
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Frontend/CompilerInvocation.cpp
Expand Up @@ -1147,6 +1147,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,

Opts.KeepStaticConsts = Args.hasArg(OPT_fkeep_static_consts);

Opts.SpeculativeLoadHardening = Args.hasArg(OPT_mspeculative_load_hardening);

return Success;
}

Expand Down
10 changes: 10 additions & 0 deletions clang/test/CodeGen/attr-speculative-load-hardening.c
@@ -0,0 +1,10 @@
// RUN: %clang_cc1 -mspeculative-load-hardening -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s -check-prefix=SLH
//
// Check that we set the attribute on each function.

int test1() {
return 42;
}
// SLH: @{{.*}}test1{{.*}}[[SLH:#[0-9]+]]

// SLH: attributes [[SLH]] = { {{.*}}speculative_load_hardening{{.*}} }
9 changes: 9 additions & 0 deletions clang/test/Driver/x86-target-features.c
Expand Up @@ -140,6 +140,15 @@
// RETPOLINE-EXTERNAL-THUNK: "-target-feature" "+retpoline-external-thunk"
// NO-RETPOLINE-EXTERNAL-THUNK: "-target-feature" "-retpoline-external-thunk"

// RUN: %clang -target i386-linux-gnu -mspeculative-load-hardening %s -### -o %t.o 2>&1 | FileCheck -check-prefix=SLH %s
// RUN: %clang -target i386-linux-gnu -mretpoline -mspeculative-load-hardening %s -### -o %t.o 2>&1 | FileCheck -check-prefix=RETPOLINE %s
// RUN: %clang -target i386-linux-gnu -mno-speculative-load-hardening %s -### -o %t.o 2>&1 | FileCheck -check-prefix=NO-SLH %s
// SLH-NOT: retpoline
// SLH: "-target-feature" "+retpoline-indirect-calls"
// SLH-NOT: retpoline
// SLH: "-mspeculative-load-hardening"
// NO-SLH-NOT: retpoline

// 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"
Expand Down
22 changes: 22 additions & 0 deletions llvm/docs/LangRef.rst
Expand Up @@ -1636,6 +1636,28 @@ example:
This attribute indicates that HWAddressSanitizer checks
(dynamic address safety analysis based on tagged pointers) are enabled for
this function.
``speculative_load_hardening``
This attribute indicates that
`Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_
should be enabled for the function body. This is a best-effort attempt to
mitigate all known speculative execution information leak vulnerabilities
that are based on the fundamental principles of modern processors'
speculative execution. These vulnerabilities are classified as "Spectre
variant #1" vulnerabilities typically. Notably, this does not attempt to
mitigate any vulnerabilities where the speculative execution and/or
prediction devices of specific processors can be *completely* undermined
(such as "Branch Target Injection", a.k.a, "Spectre variant #2"). Instead,
this is a target-independent request to harden against the completely
generic risk posed by speculative execution to incorrectly load secret data,
making it available to some micro-architectural side-channel for information
leak. For a processor without any speculative execution or predictors, this
is expected to be a no-op.

When inlining, the attribute is sticky. Inlining a function that carries
this attribute will cause the caller to gain the attribute. This is intended
to provide a maximally conservative model where the code in a function
annotated with this attribute will always (even after inlining) end up
hardened.
``speculatable``
This function attribute indicates that the function does not have any
effects besides calculating its result and does not have undefined behavior.
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Expand Up @@ -591,6 +591,7 @@ enum AttributeKindCodes {
ATTR_KIND_NOCF_CHECK = 56,
ATTR_KIND_OPT_FOR_FUZZING = 57,
ATTR_KIND_SHADOWCALLSTACK = 58,
ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
};

enum ComdatSelectionKindCodes {
Expand Down
9 changes: 9 additions & 0 deletions llvm/include/llvm/IR/Attributes.td
Expand Up @@ -176,6 +176,14 @@ def SanitizeMemory : EnumAttr<"sanitize_memory">;
/// HWAddressSanitizer is on.
def SanitizeHWAddress : EnumAttr<"sanitize_hwaddress">;

/// Speculative Load Hardening is enabled.
///
/// Note that this uses the default compatibility (always compatible during
/// inlining) and a conservative merge strategy where inlining an attributed
/// body will add the attribute to the caller. This ensures that code carrying
/// this attribute will always be lowered with hardening enabled.
def SpeculativeLoadHardening : EnumAttr<"speculative_load_hardening">;

/// Argument is swift error.
def SwiftError : EnumAttr<"swifterror">;

Expand Down Expand Up @@ -232,6 +240,7 @@ def : MergeRule<"setAND<UnsafeFPMathAttr>">;
def : MergeRule<"setOR<NoImplicitFloatAttr>">;
def : MergeRule<"setOR<NoJumpTablesAttr>">;
def : MergeRule<"setOR<ProfileSampleAccurateAttr>">;
def : MergeRule<"setOR<SpeculativeLoadHardeningAttr>">;
def : MergeRule<"adjustCallerSSPLevel">;
def : MergeRule<"adjustCallerStackProbes">;
def : MergeRule<"adjustCallerStackProbeSize">;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Expand Up @@ -678,6 +678,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(sanitize_hwaddress);
KEYWORD(sanitize_thread);
KEYWORD(sanitize_memory);
KEYWORD(speculative_load_hardening);
KEYWORD(swifterror);
KEYWORD(swiftself);
KEYWORD(uwtable);
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Expand Up @@ -1276,6 +1276,9 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
B.addAttribute(Attribute::SanitizeThread); break;
case lltok::kw_sanitize_memory:
B.addAttribute(Attribute::SanitizeMemory); break;
case lltok::kw_speculative_load_hardening:
B.addAttribute(Attribute::SpeculativeLoadHardening);
break;
case lltok::kw_strictfp: B.addAttribute(Attribute::StrictFP); break;
case lltok::kw_uwtable: B.addAttribute(Attribute::UWTable); break;
case lltok::kw_writeonly: B.addAttribute(Attribute::WriteOnly); break;
Expand Down Expand Up @@ -1619,6 +1622,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
case lltok::kw_sanitize_hwaddress:
case lltok::kw_sanitize_memory:
case lltok::kw_sanitize_thread:
case lltok::kw_speculative_load_hardening:
case lltok::kw_ssp:
case lltok::kw_sspreq:
case lltok::kw_sspstrong:
Expand Down Expand Up @@ -1715,6 +1719,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
case lltok::kw_sanitize_hwaddress:
case lltok::kw_sanitize_memory:
case lltok::kw_sanitize_thread:
case lltok::kw_speculative_load_hardening:
case lltok::kw_ssp:
case lltok::kw_sspreq:
case lltok::kw_sspstrong:
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLToken.h
Expand Up @@ -219,6 +219,7 @@ enum Kind {
kw_sret,
kw_sanitize_thread,
kw_sanitize_memory,
kw_speculative_load_hardening,
kw_strictfp,
kw_swifterror,
kw_swiftself,
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Expand Up @@ -1165,6 +1165,8 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
case Attribute::NoCfCheck: return 1ULL << 57;
case Attribute::OptForFuzzing: return 1ULL << 58;
case Attribute::ShadowCallStack: return 1ULL << 59;
case Attribute::SpeculativeLoadHardening:
return 1ULL << 60;
case Attribute::Dereferenceable:
llvm_unreachable("dereferenceable attribute not supported in raw format");
break;
Expand Down Expand Up @@ -1389,6 +1391,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::SanitizeThread;
case bitc::ATTR_KIND_SANITIZE_MEMORY:
return Attribute::SanitizeMemory;
case bitc::ATTR_KIND_SPECULATIVE_LOAD_HARDENING:
return Attribute::SpeculativeLoadHardening;
case bitc::ATTR_KIND_SWIFT_ERROR:
return Attribute::SwiftError;
case bitc::ATTR_KIND_SWIFT_SELF:
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Expand Up @@ -690,6 +690,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_SANITIZE_THREAD;
case Attribute::SanitizeMemory:
return bitc::ATTR_KIND_SANITIZE_MEMORY;
case Attribute::SpeculativeLoadHardening:
return bitc::ATTR_KIND_SPECULATIVE_LOAD_HARDENING;
case Attribute::SwiftError:
return bitc::ATTR_KIND_SWIFT_ERROR;
case Attribute::SwiftSelf:
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/IR/Attributes.cpp
Expand Up @@ -323,6 +323,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "returns_twice";
if (hasAttribute(Attribute::SExt))
return "signext";
if (hasAttribute(Attribute::SpeculativeLoadHardening))
return "speculative_load_hardening";
if (hasAttribute(Attribute::Speculatable))
return "speculatable";
if (hasAttribute(Attribute::StackProtect))
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/IR/Verifier.cpp
Expand Up @@ -1478,6 +1478,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
case Attribute::InaccessibleMemOnly:
case Attribute::InaccessibleMemOrArgMemOnly:
case Attribute::AllocSize:
case Attribute::SpeculativeLoadHardening:
case Attribute::Speculatable:
case Attribute::StrictFP:
return true;
Expand Down
11 changes: 11 additions & 0 deletions llvm/lib/Target/X86/X86SpeculativeLoadHardening.cpp
Expand Up @@ -75,6 +75,11 @@ STATISTIC(NumCallsOrJumpsHardened,
STATISTIC(NumInstsInserted, "Number of instructions inserted");
STATISTIC(NumLFENCEsInserted, "Number of lfence instructions inserted");

static cl::opt<bool> EnableSpeculativeLoadHardening(
"x86-speculative-load-hardening",
cl::desc("Force enable speculative load hardening"), cl::init(false),
cl::Hidden);

static cl::opt<bool> HardenEdgesWithLFENCE(
PASS_KEY "-lfence",
cl::desc(
Expand Down Expand Up @@ -404,6 +409,12 @@ bool X86SpeculativeLoadHardeningPass::runOnMachineFunction(
LLVM_DEBUG(dbgs() << "********** " << getPassName() << " : " << MF.getName()
<< " **********\n");

// Only run if this pass is forced enabled or we detect the relevant function
// attribute requesting SLH.
if (!EnableSpeculativeLoadHardening &&
!MF.getFunction().hasFnAttribute(Attribute::SpeculativeLoadHardening))
return false;

Subtarget = &MF.getSubtarget<X86Subtarget>();
MRI = &MF.getRegInfo();
TII = Subtarget->getInstrInfo();
Expand Down
8 changes: 1 addition & 7 deletions llvm/lib/Target/X86/X86TargetMachine.cpp
Expand Up @@ -54,10 +54,6 @@ static cl::opt<bool> EnableMachineCombinerPass("x86-machine-combiner",
cl::desc("Enable the machine combiner pass"),
cl::init(true), cl::Hidden);

static cl::opt<bool> EnableSpeculativeLoadHardening(
"x86-speculative-load-hardening",
cl::desc("Enable speculative load hardening"), cl::init(false), cl::Hidden);

namespace llvm {

void initializeWinEHStatePassPass(PassRegistry &);
Expand Down Expand Up @@ -475,9 +471,7 @@ void X86PassConfig::addPreRegAlloc() {
addPass(createX86AvoidStoreForwardingBlocks());
}

if (EnableSpeculativeLoadHardening)
addPass(createX86SpeculativeLoadHardeningPass());

addPass(createX86SpeculativeLoadHardeningPass());
addPass(createX86FlagsCopyLoweringPass());
addPass(createX86WinAllocaExpander());
}
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp
Expand Up @@ -58,6 +58,7 @@ static Attribute::AttrKind parseAttrKind(StringRef Kind) {
.Case("sanitize_hwaddress", Attribute::SanitizeHWAddress)
.Case("sanitize_memory", Attribute::SanitizeMemory)
.Case("sanitize_thread", Attribute::SanitizeThread)
.Case("speculative_load_hardening", Attribute::SpeculativeLoadHardening)
.Case("ssp", Attribute::StackProtect)
.Case("sspreq", Attribute::StackProtectReq)
.Case("sspstrong", Attribute::StackProtectStrong)
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Utils/CodeExtractor.cpp
Expand Up @@ -753,6 +753,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
case Attribute::SanitizeMemory:
case Attribute::SanitizeThread:
case Attribute::SanitizeHWAddress:
case Attribute::SpeculativeLoadHardening:
case Attribute::StackProtect:
case Attribute::StackProtectReq:
case Attribute::StackProtectStrong:
Expand Down
1 change: 1 addition & 0 deletions llvm/test/CodeGen/X86/O0-pipeline.ll
Expand Up @@ -37,6 +37,7 @@
; CHECK-NEXT: X86 PIC Global Base Reg Initialization
; CHECK-NEXT: Expand ISel Pseudo-instructions
; CHECK-NEXT: Local Stack Slot Allocation
; CHECK-NEXT: X86 speculative load hardening
; CHECK-NEXT: MachineDominator Tree Construction
; CHECK-NEXT: X86 EFLAGS copy lowering
; CHECK-NEXT: X86 WinAlloca Expander
Expand Down
1 change: 1 addition & 0 deletions llvm/test/CodeGen/X86/O3-pipeline.ll
Expand Up @@ -90,6 +90,7 @@
; CHECK-NEXT: X86 LEA Optimize
; CHECK-NEXT: X86 Optimize Call Frame
; CHECK-NEXT: X86 Avoid Store Forwarding Block
; CHECK-NEXT: X86 speculative load hardening
; CHECK-NEXT: MachineDominator Tree Construction
; CHECK-NEXT: X86 EFLAGS copy lowering
; CHECK-NEXT: X86 WinAlloca Expander
Expand Down
8 changes: 4 additions & 4 deletions llvm/test/CodeGen/X86/speculative-load-hardening-gather.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu -x86-speculative-load-hardening | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown-linux-gnu | FileCheck %s

declare <4 x float> @llvm.x86.avx2.gather.d.ps(<4 x float>, i8*, <4 x i32>, <4 x float>, i8)

Expand Down Expand Up @@ -950,6 +950,6 @@ entry:
ret <4 x i64> %v
}

attributes #0 = { nounwind "target-features"="+avx2" }
attributes #1 = { nounwind "target-features"="+avx512f" }
attributes #2 = { nounwind "target-features"="+avx512vl" }
attributes #0 = { nounwind speculative_load_hardening "target-features"="+avx2" }
attributes #1 = { nounwind speculative_load_hardening "target-features"="+avx512f" }
attributes #2 = { nounwind speculative_load_hardening "target-features"="+avx512vl" }

0 comments on commit 664aa86

Please sign in to comment.