llvm.org GIT mirror llvm / b89275f
Instrument Control Flow For Indirect Branch Tracking CET (Control-Flow Enforcement Technology) introduces a new mechanism called IBT (Indirect Branch Tracking). According to IBT, each Indirect branch should land on dedicated ENDBR instruction (End Branch). The new pass adds ENDBR instructions for every indirect jmp/call (including jumps using jump tables / switches). For more information, please see the following: https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf Differential Revision: https://reviews.llvm.org/D40482 Change-Id: Icb754489faf483a95248f96982a4e8b1009eb709 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@322062 91177308-0d34-0410-b5e6-96231b3b80d8 Oren Ben Simhon 2 years ago
8 changed file(s) with 386 addition(s) and 2 deletion(s). Raw diff Collapse all Expand all
2222 set(sources
2323 X86AsmPrinter.cpp
2424 X86CallFrameOptimization.cpp
25 X86CallingConv.cpp
2526 X86CallLowering.cpp
2627 X86CmovConversion.cpp
2728 X86DomainReassignment.cpp
3536 X86InstructionSelector.cpp
3637 X86ISelDAGToDAG.cpp
3738 X86ISelLowering.cpp
39 X86IndirectBranchTracking.cpp
3840 X86InterleavedAccess.cpp
3941 X86InstrFMA3Info.cpp
4042 X86InstrInfo.cpp
5658 X86VZeroUpper.cpp
5759 X86WinAllocaExpander.cpp
5860 X86WinEHState.cpp
59 X86CallingConv.cpp
6061 )
6162
6263 add_llvm_target(X86CodeGen ${sources})
4747 /// This pass inserts AVX vzeroupper instructions before each call to avoid
4848 /// transition penalty between functions encoded with AVX and SSE.
4949 FunctionPass *createX86IssueVZeroUpperPass();
50
51 /// This pass inserts ENDBR instructions before indirect jump/call
52 /// destinations as part of CET IBT mechanism.
53 FunctionPass *createX86IndirectBranchTrackingPass();
5054
5155 /// Return a pass that pads short functions with NOOPs.
5256 /// This will prevent a stall when returning on the Atom.
0 //===---- X86IndirectBranchTracking.cpp - Enables CET IBT mechanism -------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines a pass that enables Indirect Branch Tracking (IBT) as part
10 // of Control-Flow Enforcement Technology (CET).
11 // The pass adds ENDBR (End Branch) machine instructions at the beginning of
12 // each basic block or function that is referenced by an indrect jump/call
13 // instruction.
14 // The ENDBR instructions have a NOP encoding and as such are ignored in
15 // targets that do not support CET IBT mechanism.
16 //===----------------------------------------------------------------------===//
17
18 #include "X86.h"
19 #include "X86InstrInfo.h"
20 #include "X86Subtarget.h"
21 #include "llvm/ADT/Statistic.h"
22 #include "llvm/CodeGen/MachineFunctionPass.h"
23 #include "llvm/CodeGen/MachineInstrBuilder.h"
24 #include "llvm/CodeGen/MachineJumpTableInfo.h"
25 #include "llvm/CodeGen/MachineModuleInfo.h"
26
27 using namespace llvm;
28
29 #define DEBUG_TYPE "x86-indirect-branch-tracking"
30
31 static cl::opt IndirectBranchTracking(
32 "x86-indirect-branch-tracking", cl::init(false), cl::Hidden,
33 cl::desc("Enable X86 indirect branch tracking pass."));
34
35 STATISTIC(NumEndBranchAdded, "Number of ENDBR instructions added");
36
37 namespace {
38 class X86IndirectBranchTrackingPass : public MachineFunctionPass {
39 public:
40 X86IndirectBranchTrackingPass() : MachineFunctionPass(ID) {}
41
42 StringRef getPassName() const override {
43 return "X86 Indirect Branch Tracking";
44 }
45
46 bool runOnMachineFunction(MachineFunction &MF) override;
47
48 private:
49 static char ID;
50
51 /// Machine instruction info used throughout the class.
52 const X86InstrInfo *TII;
53
54 /// Endbr opcode for the current machine function.
55 unsigned int EndbrOpcode;
56
57 /// The function looks for an indirect jump terminator in MBB predecessors.
58 ///
59 /// Jump tables are generated when lowering switch-case statements or
60 /// setjmp/longjump functions.
61 /// As a result only indirect jumps use jump tables.
62 /// The function verifies this assumption.
63 ///
64 /// \return true if the input \p MBB has a predecessor MBB with indirect
65 /// branch terminator or false otherwise.
66 bool verifyIndirectJump(const MachineBasicBlock *MBB) const;
67
68 /// Adds a new ENDBR instruction to the begining of the MBB.
69 /// The function will not add it if already exists.
70 /// It will add ENDBR32 or ENDBR64 opcode, depending on the target.
71 void addENDBR(MachineBasicBlock &MBB) const;
72 };
73
74 } // end anonymous namespace
75
76 char X86IndirectBranchTrackingPass::ID = 0;
77
78 FunctionPass *llvm::createX86IndirectBranchTrackingPass() {
79 return new X86IndirectBranchTrackingPass();
80 }
81
82 bool X86IndirectBranchTrackingPass::verifyIndirectJump(
83 const MachineBasicBlock *MBB) const {
84 for (auto &PredMBB : MBB->predecessors())
85 for (auto &TermI : PredMBB->terminators())
86 if (TermI.isIndirectBranch())
87 return true;
88
89 return false;
90 }
91
92 void X86IndirectBranchTrackingPass::addENDBR(MachineBasicBlock &MBB) const {
93 assert(TII && "Target instruction info was not initialized");
94 assert((X86::ENDBR64 == EndbrOpcode || X86::ENDBR32 == EndbrOpcode) &&
95 "Unexpected Endbr opcode");
96
97 auto MI = MBB.begin();
98 // If the MBB is empty or the first instruction is not ENDBR,
99 // add the ENDBR instruction to the beginning of the MBB.
100 if (MI == MBB.end() || EndbrOpcode != MI->getOpcode()) {
101 BuildMI(MBB, MI, MBB.findDebugLoc(MI), TII->get(EndbrOpcode));
102 NumEndBranchAdded++;
103 }
104 }
105
106 bool X86IndirectBranchTrackingPass::runOnMachineFunction(MachineFunction &MF) {
107 const X86Subtarget &SubTarget = MF.getSubtarget();
108
109 // Make sure that the target supports ENDBR instruction.
110 if (!SubTarget.hasIBT())
111 return false;
112
113 // Check that the cf-protection-branch is enabled.
114 Metadata *isCFProtectionSupported =
115 MF.getMMI().getModule()->getModuleFlag("cf-protection-branch");
116 if (!isCFProtectionSupported && !IndirectBranchTracking)
117 return false;
118
119 // True if the current MF was changed and false otherwise.
120 bool Changed = false;
121
122 TII = SubTarget.getInstrInfo();
123 EndbrOpcode = SubTarget.is64Bit() ? X86::ENDBR64 : X86::ENDBR32;
124
125 // Non-internal function or function whose address was taken, can be
126 // invoked through indirect calls. Mark the first BB with ENDBR instruction.
127 // TODO: Do not add ENDBR instruction in case notrack attribute is used.
128 if (MF.getFunction().hasAddressTaken() ||
129 !MF.getFunction().hasLocalLinkage()) {
130 auto MBB = MF.begin();
131 addENDBR(*MBB);
132 Changed = true;
133 }
134
135 for (auto &MBB : MF) {
136 // Find all basic blocks that thier address was taken (for example
137 // in the case of indirect jump) and add ENDBR instruction.
138 if (MBB.hasAddressTaken()) {
139 addENDBR(MBB);
140 Changed = true;
141 }
142 }
143
144 // Adds ENDBR instructions to MBB destinations of the jump table.
145 // TODO: In case of more than 50 destinations, do not add ENDBR and
146 // instead add DS_PREFIX.
147 if (MachineJumpTableInfo *JTI = MF.getJumpTableInfo()) {
148 for (const auto &JT : JTI->getJumpTables()) {
149 for (auto *MBB : JT.MBBs) {
150 // This assert verifies the assumption that this MBB has an indirect
151 // jump terminator in one of its predecessor.
152 assert(verifyIndirectJump(MBB) &&
153 "The MBB is not the destination of an indirect jump");
154
155 addENDBR(*MBB);
156 Changed = true;
157 }
158 }
159 }
160
161 return Changed;
162 }
535535 } // Defs SSP
536536 } // SchedRW && HasSHSTK
537537
538 let Predicates = [HasIBT] in {
539 def ENDBR64 : I<0x1E, MRM_FA, (outs), (ins), "endbr64", []>, XS;
540 def ENDBR32 : I<0x1E, MRM_FB, (outs), (ins), "endbr32", []>, XS;
541 } // HasIBT
542
538543 //===----------------------------------------------------------------------===//
539544 // XSAVE instructions
540545 let SchedRW = [WriteSystem] in {
425425 if (getOptLevel() != CodeGenOpt::None)
426426 addPass(new X86ExecutionDepsFix());
427427
428 addPass(createX86IndirectBranchTrackingPass());
429
428430 if (UseVZeroUpper)
429431 addPass(createX86IssueVZeroUpperPass());
430432
4747 ; CHECK-NEXT: Post-RA pseudo instruction expansion pass
4848 ; CHECK-NEXT: X86 pseudo instruction expansion pass
4949 ; CHECK-NEXT: Analyze Machine Code For Garbage Collection
50 ; CHECK-NEXT: X86 Indirect Branch Tracking
5051 ; CHECK-NEXT: X86 vzeroupper inserter
5152 ; CHECK-NEXT: Contiguously Lay Out Funclets
5253 ; CHECK-NEXT: StackMap Liveness Analysis
0 ; RUN: llc -mtriple=x86_64-unknown-unknown -mattr=+ibt -x86-indirect-branch-tracking < %s | FileCheck %s --check-prefix=ALL --check-prefix=X86_64
1 ; RUN: llc -mtriple=i386-unknown-unknown -mattr=+ibt -x86-indirect-branch-tracking < %s | FileCheck %s --check-prefix=ALL --check-prefix=X86
2
3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4 ;; Test1
5 ;; -----
6 ;; Checks ENDBR insertion in case of indirect branch IR instruction.
7 ;; Also since the function is not internal, make sure that endbr32/64 was
8 ;; added at the beginning of the function.
9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10
11 define i8 @test1(){
12 ; ALL-LABEL: test1
13 ; X86_64: endbr64
14 ; X86: endbr32
15 ; ALL: jmp{{q|l}} *
16 ; ALL: .LBB0_1:
17 ; X86_64-NEXT: endbr64
18 ; X86-NEXT: endbr32
19 ; ALL: .LBB0_2:
20 ; X86_64-NEXT: endbr64
21 ; X86-NEXT: endbr32
22 entry:
23 %0 = select i1 undef, i8* blockaddress(@test1, %bb), i8* blockaddress(@test1, %bb6) ; [#uses=1]
24 indirectbr i8* %0, [label %bb, label %bb6]
25
26 bb: ; preds = %entry
27 ret i8 1
28
29 bb6: ; preds = %entry
30 ret i8 2
31 }
32
33 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
34 ;; Test2
35 ;; -----
36 ;; Checks ENDBR insertion in case of switch case statement.
37 ;; Also since the function is not internal, ENDBR instruction should be
38 ;; added to its first basic block.
39 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
40
41 define i32 @test2(i32 %a) {
42 ; ALL-LABEL: test2
43 ; X86_64: endbr64
44 ; X86: endbr32
45 ; ALL: jmp{{q|l}} *
46 ; ALL: .LBB1_2:
47 ; X86_64-NEXT: endbr64
48 ; X86-NEXT: endbr32
49 ; ALL: .LBB1_7:
50 ; X86_64-NOT: endbr64
51 ; X86-NOT: endbr32
52 ; ALL: .LBB1_3:
53 ; X86_64-NEXT: endbr64
54 ; X86-NEXT: endbr32
55 ; ALL: .LBB1_4:
56 ; X86_64-NEXT: endbr64
57 ; X86-NEXT: endbr32
58 ; ALL: .LBB1_5:
59 ; X86_64-NEXT: endbr64
60 ; X86-NEXT: endbr32
61 ; ALL: .LBB1_6:
62 ; X86_64-NEXT: endbr64
63 ; X86-NEXT: endbr32
64 entry:
65 %retval = alloca i32, align 4
66 %a.addr = alloca i32, align 4
67 store i32 %a, i32* %a.addr, align 4
68 %0 = load i32, i32* %a.addr, align 4
69 switch i32 %0, label %sw.default [
70 i32 0, label %sw.bb
71 i32 1, label %sw.bb1
72 i32 2, label %sw.bb2
73 i32 3, label %sw.bb3
74 i32 4, label %sw.bb4
75 ]
76
77 sw.bb: ; preds = %entry
78 store i32 5, i32* %retval, align 4
79 br label %return
80
81 sw.bb1: ; preds = %entry
82 store i32 7, i32* %retval, align 4
83 br label %return
84
85 sw.bb2: ; preds = %entry
86 store i32 2, i32* %retval, align 4
87 br label %return
88
89 sw.bb3: ; preds = %entry
90 store i32 32, i32* %retval, align 4
91 br label %return
92
93 sw.bb4: ; preds = %entry
94 store i32 73, i32* %retval, align 4
95 br label %return
96
97 sw.default: ; preds = %entry
98 store i32 0, i32* %retval, align 4
99 br label %return
100
101 return: ; preds = %sw.default, %sw.bb4, %sw.bb3, %sw.bb2, %sw.bb1, %sw.bb
102 %1 = load i32, i32* %retval, align 4
103 ret i32 %1
104 }
105
106 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
107 ;; Test3
108 ;; -----
109 ;; Checks ENDBR insertion in case of indirect call instruction.
110 ;; The new instruction should be added to the called function (test6)
111 ;; although it is internal.
112 ;; Also since the function is not internal, ENDBR instruction should be
113 ;; added to its first basic block.
114 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
115
116 define void @test3() {
117 ; ALL-LABEL: test3
118 ; X86_64: endbr64
119 ; X86: endbr32
120 ; ALL: call{{q|l}} *
121 entry:
122 %f = alloca i32 (...)*, align 8
123 store i32 (...)* bitcast (i32 (i32)* @test6 to i32 (...)*), i32 (...)** %f, align 8
124 %0 = load i32 (...)*, i32 (...)** %f, align 8
125 %call = call i32 (...) %0()
126 ret void
127 }
128
129 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
130 ;; Test4
131 ;; -----
132 ;; Checks ENDBR insertion in case of setjmp-like function calls.
133 ;; Also since the function is not internal, ENDBR instruction should be
134 ;; added to its first basic block.
135 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
136
137 @buf = internal global [5 x i8*] zeroinitializer
138 declare i8* @llvm.frameaddress(i32)
139 declare i8* @llvm.stacksave()
140 declare i32 @llvm.eh.sjlj.setjmp(i8*)
141
142 define i32 @test4() {
143 ; ALL-LABEL: test4
144 ; X86_64: endbr64
145 ; X86: endbr32
146 ; ALL: .LBB3_3:
147 ; X86_64-NEXT: endbr64
148 ; X86-NEXT: endbr32
149 %fp = tail call i8* @llvm.frameaddress(i32 0)
150 store i8* %fp, i8** getelementptr inbounds ([5 x i8*], [5 x i8*]* @buf, i64 0, i64 0), align 16
151 %sp = tail call i8* @llvm.stacksave()
152 store i8* %sp, i8** getelementptr inbounds ([5 x i8*], [5 x i8*]* @buf, i64 0, i64 2), align 16
153 %r = tail call i32 @llvm.eh.sjlj.setjmp(i8* bitcast ([5 x i8*]* @buf to i8*))
154 ret i32 %r
155 }
156
157 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
158 ;; Test5
159 ;; -----
160 ;; Checks ENDBR insertion in case of internal function.
161 ;; Since the function is internal and its address was not taken,
162 ;; make sure that endbr32/64 was not added at the beginning of the
163 ;; function.
164 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
165
166 define internal i8 @test5(){
167 ; ALL-LABEL: test5
168 ; X86_64-NOT: endbr64
169 ; X86-NOT: endbr32
170 ret i8 1
171 }
172
173 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
174 ;; Test6
175 ;; -----
176 ;; Checks ENDBR insertion in case of function that its was address taken.
177 ;; Since the function's address was taken by test3() and despite being
178 ;; internal, check for added endbr32/64 at the beginning of the function.
179 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
180
181 define internal i32 @test6(i32 %a) {
182 ; ALL-LABEL: test6
183 ; X86_64: endbr64
184 ; X86: endbr32
185 ret i32 1
186 }
187
188 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
189 ;; Test7
190 ;; -----
191 ;; Checks ENDBR insertion in case of non-intrenal function.
192 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
193
194 define i32 @test7() {
195 ; ALL-LABEL: test7
196 ; X86_64: endbr64
197 ; X86: endbr32
198 ret i32 1
199 }
None // RUN: llvm-mc -triple x86_64-unknown-unknown -mattr=+shstk --show-encoding %s | FileCheck %s
0 // RUN: llvm-mc -triple x86_64-unknown-unknown -mattr=+shstk -mattr=+ibt --show-encoding %s | FileCheck %s
11
22 // CHECK: incsspd %r13d
33 // CHECK: # encoding: [0xf3,0x41,0x0f,0xae,0xed]
166166 // CHECK: setssbsy
167167 // CHECK: # encoding: [0xf3,0x0f,0x01,0xe8]
168168 setssbsy
169
170 // CHECK: endbr64
171 // CHECK: # encoding: [0xf3,0x0f,0x1e,0xfa]
172 endbr64
173
174 // CHECK: endbr32
175 // CHECK: # encoding: [0xf3,0x0f,0x1e,0xfb]
176 endbr32