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