|
| 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(NewRetMBB->end(), CurMBB, SplitIt, 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(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(0); |
| 89 | + if (MO.isGlobal()) { |
| 90 | + auto Callee = dyn_cast<Function>(MO.getGlobal()); |
| 91 | + if (Callee && Callee->getName() == "__security_check_cookie") { |
| 92 | + return std::make_pair(&MBB, &MI); |
| 93 | + break; |
| 94 | + } |
| 95 | + } |
| 96 | + } |
| 97 | + } |
| 98 | + } |
| 99 | + return std::make_pair(nullptr, 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("__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(*CurMBB, InsertPt, DebugLoc(), TII->get(X86::CMP64rm)) |
| 152 | + .addReg(GuardXor->getOperand(0).getReg()) |
| 153 | + .addReg(X86::RIP) |
| 154 | + .addImm(1) |
| 155 | + .addReg(X86::NoRegister) |
| 156 | + .addGlobalAddress(GV) |
| 157 | + .addReg(X86::NoRegister); |
| 158 | + |
| 159 | + BuildMI(*CurMBB, InsertPt, DebugLoc(), TII->get(X86::JCC_1)) |
| 160 | + .addMBB(FailMBB) |
| 161 | + .addImm(X86::COND_NE); |
| 162 | + |
| 163 | + auto JMI = BuildMI(*CurMBB, InsertPt, DebugLoc(), TII->get(X86::JMP_1)); |
| 164 | + |
| 165 | + return std::make_pair(CMI.getInstr(), JMI.getInstr()); |
| 166 | +} |
| 167 | + |
| 168 | +void X86WinFixupBufferSecurityCheckPass::FinishBlock(MachineBasicBlock *MBB) { |
| 169 | + LivePhysRegs LiveRegs; |
| 170 | + computeAndAddLiveIns(LiveRegs, *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(FailMBB); |
| 182 | + FinishBlock(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("__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(MF.end(), NewRetMBB); |
| 211 | + MF.insert(MF.end(), 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(FailMBB->end(), CurMBB, U1It, U2It); |
| 231 | + BuildMI(*FailMBB, FailMBB->end(), DebugLoc(), TII->get(X86::INT3)); |
| 232 | + |
| 233 | + // Move left over instruction after StackUp |
| 234 | + // from Current Basic BLocks into New Return Block |
| 235 | + JMI.addMBB(NewRetMBB); |
| 236 | + MachineBasicBlock::iterator SplicePt(JMI.getInstr()); |
| 237 | + ++SplicePt; |
| 238 | + if (SplicePt != CurMBB->end()) |
| 239 | + NewRetMBB->splice(NewRetMBB->end(), CurMBB, SplicePt); |
| 240 | + |
| 241 | + // Restructure Basic Blocks |
| 242 | + CurMBB->addSuccessor(NewRetMBB); |
| 243 | + CurMBB->addSuccessor(FailMBB); |
| 244 | + |
| 245 | + FinishFunction(FailMBB, NewRetMBB); |
| 246 | + return !Changed; |
| 247 | +} |
0 commit comments