diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -368,6 +368,8 @@ X86 Support in Clang -------------------- +- Support ``-mharden-sls=all`` for X86. + DWARF Support in Clang ---------------------- 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 @@ -242,4 +242,11 @@ Name = Name.substr(3); Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); } + + // Enable/disable straight line speculation hardening. + if (Arg *A = Args.getLastArg(options::OPT_mharden_sls_EQ)) { + StringRef Scope = A->getValue(); + if (Scope == "all") + Features.push_back("+harden-sls-all"); + } } 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 @@ -304,3 +304,8 @@ // RUN: %clang -target i386-unknown-linux-gnu -march=i386 -mno-crc32 %s -### -o %t.o 2>&1 | FileCheck -check-prefix=NO-CRC32 %s // CRC32: "-target-feature" "+crc32" // NO-CRC32: "-target-feature" "-crc32" + +// RUN: %clang -target i386-unknown-linux-gnu -march=i386 -mharden-sls=all %s -### -o %t.o 2>&1 | FileCheck -check-prefix=SLS %s +// RUN: %clang -target i386-unknown-linux-gnu -march=i386 -mharden-sls=none %s -### -o %t.o 2>&1 | FileCheck -check-prefix=NO-SLS %s +// SLS: "-target-feature" "+harden-sls-all" +// NO-SLS-NOT: "harden-sls" 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 @@ -382,6 +382,12 @@ "Use an instruction sequence for taking the address of a global " "that allows a memory tag in the upper address bits.">; +// Control codegen mitigation against Straight Line Speculation vulnerability. +def FeatureHardenSlsAll + : SubtargetFeature< + "harden-sls-all", "HardenSlsAll", "true", + "Harden against straight line speculation across RET and BR instructions.">; + //===----------------------------------------------------------------------===// // X86 Subtarget Tuning features //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/X86/X86AsmPrinter.h b/llvm/lib/Target/X86/X86AsmPrinter.h --- a/llvm/lib/Target/X86/X86AsmPrinter.h +++ b/llvm/lib/Target/X86/X86AsmPrinter.h @@ -131,10 +131,7 @@ void emitInstruction(const MachineInstr *MI) override; - void emitBasicBlockEnd(const MachineBasicBlock &MBB) override { - AsmPrinter::emitBasicBlockEnd(MBB); - SMShadowTracker.emitShadowPadding(*OutStreamer, getSubtargetInfo()); - } + void emitBasicBlockEnd(const MachineBasicBlock &MBB) override; bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *ExtraCode, raw_ostream &O) override; diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -336,6 +336,16 @@ } } +void X86AsmPrinter::emitBasicBlockEnd(const MachineBasicBlock &MBB) { + AsmPrinter::emitBasicBlockEnd(MBB); + if (Subtarget->hardenSlsAll() && !MBB.back().getDesc().isIndirectBranch()) { + MCInst TmpInst; + TmpInst.setOpcode(X86::INT3); + EmitToStreamer(*OutStreamer, TmpInst); + } + SMShadowTracker.emitShadowPadding(*OutStreamer, getSubtargetInfo()); +} + void X86AsmPrinter::PrintMemReference(const MachineInstr *MI, unsigned OpNo, raw_ostream &O, const char *Modifier) { assert(isMem(*MI, OpNo) && "Invalid memory reference!"); diff --git a/llvm/test/CodeGen/X86/speculation-hardening-sls.ll b/llvm/test/CodeGen/X86/speculation-hardening-sls.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/speculation-hardening-sls.ll @@ -0,0 +1,93 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mattr=harden-sls-all -verify-machineinstrs -mtriple=x86_64-unknown-unknown < %s | FileCheck %s + +define dso_local i32 @double_return(i32 %a, i32 %b) local_unnamed_addr { +; CHECK-LABEL: double_return: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: testl %edi, %edi +; CHECK-NEXT: jle .LBB0_2 +; CHECK-NEXT: int3 +; CHECK-NEXT: # %bb.1: # %if.then +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: cltd +; CHECK-NEXT: idivl %esi +; CHECK-NEXT: retq +; CHECK-NEXT: int3 +; CHECK-NEXT: .LBB0_2: # %if.else +; CHECK-NEXT: movl %esi, %eax +; CHECK-NEXT: cltd +; CHECK-NEXT: idivl %edi +; CHECK-NEXT: retq +; CHECK-NEXT: int3 +entry: + %cmp = icmp sgt i32 %a, 0 + br i1 %cmp, label %if.then, label %if.else + +if.then: ; preds = %entry + %div = sdiv i32 %a, %b + ret i32 %div + +if.else: ; preds = %entry + %div1 = sdiv i32 %b, %a + ret i32 %div1 +} + +@__const.indirect_branch.ptr = private unnamed_addr constant [2 x i8*] [i8* blockaddress(@indirect_branch, %return), i8* blockaddress(@indirect_branch, %l2)], align 8 + +; Function Attrs: norecurse nounwind readnone +define dso_local i32 @indirect_branch(i32 %a, i32 %b, i32 %i) { +; CHECK-LABEL: indirect_branch: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: movslq %edx, %rax +; CHECK-NEXT: jmpq *.L__const.indirect_branch.ptr(,%rax,8) +; CHECK-NEXT: .Ltmp0: # Block address taken +; CHECK-NEXT: .LBB1_2: # %return +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +; CHECK-NEXT: int3 +; CHECK-NEXT: .Ltmp1: # Block address taken +; CHECK-NEXT: .LBB1_1: # %l2 +; CHECK-NEXT: movl $1, %eax +; CHECK-NEXT: retq +; CHECK-NEXT: int3 +entry: + %idxprom = sext i32 %i to i64 + %arrayidx = getelementptr inbounds [2 x i8*], [2 x i8*]* @__const.indirect_branch.ptr, i64 0, i64 %idxprom + %0 = load i8*, i8** %arrayidx, align 8 + indirectbr i8* %0, [label %return, label %l2] + +l2: ; preds = %entry + br label %return + +return: ; preds = %entry, %l2 + %retval.0 = phi i32 [ 1, %l2 ], [ 0, %entry ] + ret i32 %retval.0 +} + +define i32 @asmgoto() { +; CHECK-LABEL: asmgoto: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: #APP +; CHECK-NEXT: jmp .Ltmp2 +; CHECK-NEXT: #NO_APP +; CHECK-NEXT: int3 +; CHECK-NEXT: # %bb.1: # %asm.fallthrough +; CHECK-NEXT: xorl %eax, %eax +; CHECK-NEXT: retq +; CHECK-NEXT: int3 +; CHECK-NEXT: .Ltmp2: # Block address taken +; CHECK-NEXT: .LBB2_2: # %d +; CHECK-NEXT: movl $1, %eax +; CHECK-NEXT: retq +; CHECK-NEXT: int3 +entry: + callbr void asm sideeffect "jmp $0", "X"(i8* blockaddress(@asmgoto, %d)) + to label %asm.fallthrough [label %d] + ; The asm goto above produces a direct branch: + +asm.fallthrough: ; preds = %entry + ret i32 0 + +d: ; preds = %asm.fallthrough, %entry + ret i32 1 +}