1 | //===- X86WinFixupBufferSecurityCheck.cpp Fix Buffer Security Check Call -===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // Buffer Security Check implementation inserts windows specific callback into |
9 | // code. On windows, __security_check_cookie call gets call everytime function |
10 | // is return without fixup. Since this function is defined in runtime library, |
11 | // it incures cost of call in dll which simply does comparison and returns most |
12 | // time. With Fixup, We selective move to call in DLL only if comparison fails. |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #include "X86.h" |
16 | #include "X86FrameLowering.h" |
17 | #include "X86InstrInfo.h" |
18 | #include "X86Subtarget.h" |
19 | #include "llvm/CodeGen/LivePhysRegs.h" |
20 | #include "llvm/CodeGen/MachineFunctionPass.h" |
21 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
22 | #include "llvm/CodeGen/MachineRegisterInfo.h" |
23 | #include "llvm/IR/Module.h" |
24 | #include <iterator> |
25 | |
26 | using namespace llvm; |
27 | |
28 | #define DEBUG_TYPE "x86-win-fixup-bscheck" |
29 | |
30 | namespace { |
31 | |
32 | class X86WinFixupBufferSecurityCheckPass : public MachineFunctionPass { |
33 | public: |
34 | static char ID; |
35 | |
36 | X86WinFixupBufferSecurityCheckPass() : MachineFunctionPass(ID) {} |
37 | |
38 | StringRef getPassName() const override { |
39 | return "X86 Windows Fixup Buffer Security Check" ; |
40 | } |
41 | |
42 | bool runOnMachineFunction(MachineFunction &MF) override; |
43 | |
44 | std::pair<MachineBasicBlock *, MachineInstr *> |
45 | getSecurityCheckerBasicBlock(MachineFunction &MF); |
46 | |
47 | void getGuardCheckSequence(MachineBasicBlock *CurMBB, MachineInstr *CheckCall, |
48 | MachineInstr *SeqMI[5]); |
49 | |
50 | void SplitBasicBlock(MachineBasicBlock *CurMBB, MachineBasicBlock *NewRetMBB, |
51 | MachineBasicBlock::iterator SplitIt); |
52 | |
53 | void FinishBlock(MachineBasicBlock *MBB); |
54 | |
55 | void FinishFunction(MachineBasicBlock *FailMBB, MachineBasicBlock *NewRetMBB); |
56 | |
57 | std::pair<MachineInstr *, MachineInstr *> |
58 | CreateFailCheckSequence(MachineBasicBlock *CurMBB, MachineBasicBlock *FailMBB, |
59 | MachineInstr *SeqMI[5]); |
60 | }; |
61 | } // end anonymous namespace |
62 | |
63 | char X86WinFixupBufferSecurityCheckPass::ID = 0; |
64 | |
65 | INITIALIZE_PASS(X86WinFixupBufferSecurityCheckPass, DEBUG_TYPE, DEBUG_TYPE, |
66 | false, false) |
67 | |
68 | FunctionPass *llvm::createX86WinFixupBufferSecurityCheckPass() { |
69 | return new X86WinFixupBufferSecurityCheckPass(); |
70 | } |
71 | |
72 | void X86WinFixupBufferSecurityCheckPass::SplitBasicBlock( |
73 | MachineBasicBlock *CurMBB, MachineBasicBlock *NewRetMBB, |
74 | MachineBasicBlock::iterator SplitIt) { |
75 | NewRetMBB->splice(Where: NewRetMBB->end(), Other: CurMBB, From: SplitIt, To: CurMBB->end()); |
76 | } |
77 | |
78 | std::pair<MachineBasicBlock *, MachineInstr *> |
79 | X86WinFixupBufferSecurityCheckPass::getSecurityCheckerBasicBlock( |
80 | MachineFunction &MF) { |
81 | MachineBasicBlock::reverse_iterator RBegin, REnd; |
82 | |
83 | for (auto &MBB : llvm::reverse(C&: MF)) { |
84 | for (RBegin = MBB.rbegin(), REnd = MBB.rend(); RBegin != REnd; ++RBegin) { |
85 | auto &MI = *RBegin; |
86 | if (MI.getOpcode() == X86::CALL64pcrel32 && |
87 | MI.getNumExplicitOperands() == 1) { |
88 | auto MO = MI.getOperand(i: 0); |
89 | if (MO.isGlobal()) { |
90 | auto Callee = dyn_cast<Function>(Val: MO.getGlobal()); |
91 | if (Callee && Callee->getName() == "__security_check_cookie" ) { |
92 | return std::make_pair(x: &MBB, y: &MI); |
93 | break; |
94 | } |
95 | } |
96 | } |
97 | } |
98 | } |
99 | return std::make_pair(x: nullptr, y: nullptr); |
100 | } |
101 | |
102 | void X86WinFixupBufferSecurityCheckPass::getGuardCheckSequence( |
103 | MachineBasicBlock *CurMBB, MachineInstr *CheckCall, |
104 | MachineInstr *SeqMI[5]) { |
105 | |
106 | MachineBasicBlock::iterator UIt(CheckCall); |
107 | MachineBasicBlock::reverse_iterator DIt(CheckCall); |
108 | // Seq From StackUp to Stack Down Is fixed. |
109 | // ADJCALLSTACKUP64 |
110 | ++UIt; |
111 | SeqMI[4] = &*UIt; |
112 | |
113 | // CALL __security_check_cookie |
114 | SeqMI[3] = CheckCall; |
115 | |
116 | // COPY function slot cookie |
117 | ++DIt; |
118 | SeqMI[2] = &*DIt; |
119 | |
120 | // ADJCALLSTACKDOWN64 |
121 | ++DIt; |
122 | SeqMI[1] = &*DIt; |
123 | |
124 | MachineBasicBlock::reverse_iterator XIt(SeqMI[1]); |
125 | for (; XIt != CurMBB->rbegin(); ++XIt) { |
126 | auto &CI = *XIt; |
127 | if ((CI.getOpcode() == X86::XOR64_FP) || (CI.getOpcode() == X86::XOR32_FP)) |
128 | break; |
129 | } |
130 | SeqMI[0] = &*XIt; |
131 | } |
132 | |
133 | std::pair<MachineInstr *, MachineInstr *> |
134 | X86WinFixupBufferSecurityCheckPass::CreateFailCheckSequence( |
135 | MachineBasicBlock *CurMBB, MachineBasicBlock *FailMBB, |
136 | MachineInstr *SeqMI[5]) { |
137 | |
138 | auto MF = CurMBB->getParent(); |
139 | |
140 | Module &M = *MF->getFunction().getParent(); |
141 | GlobalVariable *GV = M.getGlobalVariable(Name: "__security_cookie" ); |
142 | assert(GV && " Security Cookie was not installed!" ); |
143 | |
144 | const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); |
145 | |
146 | MachineInstr *GuardXor = SeqMI[0]; |
147 | MachineBasicBlock::iterator InsertPt(GuardXor); |
148 | ++InsertPt; |
149 | |
150 | // Compare security_Cookie with XOR_Val, if not same, we have violation |
151 | auto CMI = BuildMI(BB&: *CurMBB, I: InsertPt, MIMD: DebugLoc(), MCID: TII->get(Opcode: X86::CMP64rm)) |
152 | .addReg(RegNo: GuardXor->getOperand(i: 0).getReg()) |
153 | .addReg(RegNo: X86::RIP) |
154 | .addImm(Val: 1) |
155 | .addReg(RegNo: X86::NoRegister) |
156 | .addGlobalAddress(GV) |
157 | .addReg(RegNo: X86::NoRegister); |
158 | |
159 | BuildMI(BB&: *CurMBB, I: InsertPt, MIMD: DebugLoc(), MCID: TII->get(Opcode: X86::JCC_1)) |
160 | .addMBB(MBB: FailMBB) |
161 | .addImm(Val: X86::COND_NE); |
162 | |
163 | auto JMI = BuildMI(BB&: *CurMBB, I: InsertPt, MIMD: DebugLoc(), MCID: TII->get(Opcode: X86::JMP_1)); |
164 | |
165 | return std::make_pair(x: CMI.getInstr(), y: JMI.getInstr()); |
166 | } |
167 | |
168 | void X86WinFixupBufferSecurityCheckPass::FinishBlock(MachineBasicBlock *MBB) { |
169 | LivePhysRegs LiveRegs; |
170 | computeAndAddLiveIns(LiveRegs, MBB&: *MBB); |
171 | } |
172 | |
173 | void X86WinFixupBufferSecurityCheckPass::FinishFunction( |
174 | MachineBasicBlock *FailMBB, MachineBasicBlock *NewRetMBB) { |
175 | FailMBB->getParent()->RenumberBlocks(); |
176 | // FailMBB includes call to MSCV RT where is __security_check_cookie |
177 | // function is called. This function uses regcall and it expects cookie |
178 | // value from stack slot.( even if this is modified) |
179 | // Before going further we compute back livein for this block to make sure |
180 | // it is live and provided. |
181 | FinishBlock(MBB: FailMBB); |
182 | FinishBlock(MBB: NewRetMBB); |
183 | } |
184 | |
185 | bool X86WinFixupBufferSecurityCheckPass::runOnMachineFunction( |
186 | MachineFunction &MF) { |
187 | bool Changed = false; |
188 | const X86Subtarget &STI = MF.getSubtarget<X86Subtarget>(); |
189 | |
190 | if (!(STI.isTargetWindowsItanium() || STI.isTargetWindowsMSVC())) |
191 | return Changed; |
192 | |
193 | // Check if security cookie was installed or not |
194 | Module &M = *MF.getFunction().getParent(); |
195 | GlobalVariable *GV = M.getGlobalVariable(Name: "__security_cookie" ); |
196 | if (!GV) |
197 | return Changed; |
198 | |
199 | const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); |
200 | |
201 | // Check if security check cookie was installed or not |
202 | auto [CurMBB, CheckCall] = getSecurityCheckerBasicBlock(MF); |
203 | |
204 | if (!CheckCall) |
205 | return Changed; |
206 | |
207 | MachineBasicBlock *FailMBB = MF.CreateMachineBasicBlock(); |
208 | MachineBasicBlock *NewRetMBB = MF.CreateMachineBasicBlock(); |
209 | |
210 | MF.insert(MBBI: MF.end(), MBB: NewRetMBB); |
211 | MF.insert(MBBI: MF.end(), MBB: FailMBB); |
212 | |
213 | MachineInstr *SeqMI[5]; |
214 | getGuardCheckSequence(CurMBB, CheckCall, SeqMI); |
215 | // MachineInstr * GuardXor = SeqMI[0]; |
216 | |
217 | auto FailSeqRange = CreateFailCheckSequence(CurMBB, FailMBB, SeqMI); |
218 | MachineInstrBuilder JMI(MF, FailSeqRange.second); |
219 | |
220 | // After Inserting JMP_1, we can not have two terminators |
221 | // in same block, split CurrentMBB after JMP_1 |
222 | MachineBasicBlock::iterator SplitIt(SeqMI[4]); |
223 | ++SplitIt; |
224 | SplitBasicBlock(CurMBB, NewRetMBB, SplitIt); |
225 | |
226 | // Fill up Failure Routine, move Fail Check Squence from CurMBB to FailMBB |
227 | MachineBasicBlock::iterator U1It(SeqMI[1]); |
228 | MachineBasicBlock::iterator U2It(SeqMI[4]); |
229 | ++U2It; |
230 | FailMBB->splice(Where: FailMBB->end(), Other: CurMBB, From: U1It, To: U2It); |
231 | BuildMI(BB&: *FailMBB, I: FailMBB->end(), MIMD: DebugLoc(), MCID: TII->get(Opcode: X86::INT3)); |
232 | |
233 | // Move left over instruction after StackUp |
234 | // from Current Basic BLocks into New Return Block |
235 | JMI.addMBB(MBB: NewRetMBB); |
236 | MachineBasicBlock::iterator SplicePt(JMI.getInstr()); |
237 | ++SplicePt; |
238 | if (SplicePt != CurMBB->end()) |
239 | NewRetMBB->splice(Where: NewRetMBB->end(), Other: CurMBB, From: SplicePt); |
240 | |
241 | // Restructure Basic Blocks |
242 | CurMBB->addSuccessor(Succ: NewRetMBB); |
243 | CurMBB->addSuccessor(Succ: FailMBB); |
244 | |
245 | FinishFunction(FailMBB, NewRetMBB); |
246 | return !Changed; |
247 | } |
248 | |