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