10b57cec5SDimitry Andric //===---- X86IndirectBranchTracking.cpp - Enables CET IBT mechanism -------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines a pass that enables Indirect Branch Tracking (IBT) as part 100b57cec5SDimitry Andric // of Control-Flow Enforcement Technology (CET). 110b57cec5SDimitry Andric // The pass adds ENDBR (End Branch) machine instructions at the beginning of 120b57cec5SDimitry Andric // each basic block or function that is referenced by an indrect jump/call 130b57cec5SDimitry Andric // instruction. 140b57cec5SDimitry Andric // The ENDBR instructions have a NOP encoding and as such are ignored in 150b57cec5SDimitry Andric // targets that do not support CET IBT mechanism. 160b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 170b57cec5SDimitry Andric 180b57cec5SDimitry Andric #include "X86.h" 190b57cec5SDimitry Andric #include "X86InstrInfo.h" 200b57cec5SDimitry Andric #include "X86Subtarget.h" 21d65cd7a5SDimitry Andric #include "X86TargetMachine.h" 220b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h" 230b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h" 240b57cec5SDimitry Andric #include "llvm/CodeGen/MachineInstrBuilder.h" 250b57cec5SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h" 260b57cec5SDimitry Andric 270b57cec5SDimitry Andric using namespace llvm; 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric #define DEBUG_TYPE "x86-indirect-branch-tracking" 300b57cec5SDimitry Andric 31e8d8bef9SDimitry Andric cl::opt<bool> IndirectBranchTracking( 320b57cec5SDimitry Andric "x86-indirect-branch-tracking", cl::init(false), cl::Hidden, 330b57cec5SDimitry Andric cl::desc("Enable X86 indirect branch tracking pass.")); 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric STATISTIC(NumEndBranchAdded, "Number of ENDBR instructions added"); 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric namespace { 380b57cec5SDimitry Andric class X86IndirectBranchTrackingPass : public MachineFunctionPass { 390b57cec5SDimitry Andric public: 400b57cec5SDimitry Andric X86IndirectBranchTrackingPass() : MachineFunctionPass(ID) {} 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric StringRef getPassName() const override { 430b57cec5SDimitry Andric return "X86 Indirect Branch Tracking"; 440b57cec5SDimitry Andric } 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric private: 490b57cec5SDimitry Andric static char ID; 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric /// Machine instruction info used throughout the class. 52480093f4SDimitry Andric const X86InstrInfo *TII = nullptr; 530b57cec5SDimitry Andric 540b57cec5SDimitry Andric /// Endbr opcode for the current machine function. 55480093f4SDimitry Andric unsigned int EndbrOpcode = 0; 560b57cec5SDimitry Andric 57480093f4SDimitry Andric /// Adds a new ENDBR instruction to the beginning of the MBB. 580b57cec5SDimitry Andric /// The function will not add it if already exists. 590b57cec5SDimitry Andric /// It will add ENDBR32 or ENDBR64 opcode, depending on the target. 600b57cec5SDimitry Andric /// \returns true if the ENDBR was added and false otherwise. 610b57cec5SDimitry Andric bool addENDBR(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const; 620b57cec5SDimitry Andric }; 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric } // end anonymous namespace 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric char X86IndirectBranchTrackingPass::ID = 0; 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric FunctionPass *llvm::createX86IndirectBranchTrackingPass() { 690b57cec5SDimitry Andric return new X86IndirectBranchTrackingPass(); 700b57cec5SDimitry Andric } 710b57cec5SDimitry Andric 720b57cec5SDimitry Andric bool X86IndirectBranchTrackingPass::addENDBR( 730b57cec5SDimitry Andric MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const { 740b57cec5SDimitry Andric assert(TII && "Target instruction info was not initialized"); 750b57cec5SDimitry Andric assert((X86::ENDBR64 == EndbrOpcode || X86::ENDBR32 == EndbrOpcode) && 760b57cec5SDimitry Andric "Unexpected Endbr opcode"); 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric // If the MBB/I is empty or the current instruction is not ENDBR, 790b57cec5SDimitry Andric // insert ENDBR instruction to the location of I. 800b57cec5SDimitry Andric if (I == MBB.end() || I->getOpcode() != EndbrOpcode) { 810b57cec5SDimitry Andric BuildMI(MBB, I, MBB.findDebugLoc(I), TII->get(EndbrOpcode)); 820b57cec5SDimitry Andric ++NumEndBranchAdded; 830b57cec5SDimitry Andric return true; 840b57cec5SDimitry Andric } 850b57cec5SDimitry Andric return false; 860b57cec5SDimitry Andric } 870b57cec5SDimitry Andric 888bcb0991SDimitry Andric static bool IsCallReturnTwice(llvm::MachineOperand &MOp) { 890b57cec5SDimitry Andric if (!MOp.isGlobal()) 900b57cec5SDimitry Andric return false; 910b57cec5SDimitry Andric auto *CalleeFn = dyn_cast<Function>(MOp.getGlobal()); 920b57cec5SDimitry Andric if (!CalleeFn) 930b57cec5SDimitry Andric return false; 940b57cec5SDimitry Andric AttributeList Attrs = CalleeFn->getAttributes(); 95349cc55cSDimitry Andric return Attrs.hasFnAttr(Attribute::ReturnsTwice); 960b57cec5SDimitry Andric } 970b57cec5SDimitry Andric 980b57cec5SDimitry Andric bool X86IndirectBranchTrackingPass::runOnMachineFunction(MachineFunction &MF) { 990b57cec5SDimitry Andric const X86Subtarget &SubTarget = MF.getSubtarget<X86Subtarget>(); 1000b57cec5SDimitry Andric 1010b57cec5SDimitry Andric // Check that the cf-protection-branch is enabled. 1020b57cec5SDimitry Andric Metadata *isCFProtectionSupported = 1030b57cec5SDimitry Andric MF.getMMI().getModule()->getModuleFlag("cf-protection-branch"); 104d65cd7a5SDimitry Andric // NB: We need to enable IBT in jitted code if JIT compiler is CET 105d65cd7a5SDimitry Andric // enabled. 106d65cd7a5SDimitry Andric const X86TargetMachine *TM = 107d65cd7a5SDimitry Andric static_cast<const X86TargetMachine *>(&MF.getTarget()); 108d65cd7a5SDimitry Andric #ifdef __CET__ 109d65cd7a5SDimitry Andric bool isJITwithCET = TM->isJIT(); 110d65cd7a5SDimitry Andric #else 111d65cd7a5SDimitry Andric bool isJITwithCET = false; 112d65cd7a5SDimitry Andric #endif 113d65cd7a5SDimitry Andric if (!isCFProtectionSupported && !IndirectBranchTracking && !isJITwithCET) 1140b57cec5SDimitry Andric return false; 1150b57cec5SDimitry Andric 1160b57cec5SDimitry Andric // True if the current MF was changed and false otherwise. 1170b57cec5SDimitry Andric bool Changed = false; 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric TII = SubTarget.getInstrInfo(); 1200b57cec5SDimitry Andric EndbrOpcode = SubTarget.is64Bit() ? X86::ENDBR64 : X86::ENDBR32; 1210b57cec5SDimitry Andric 122d65cd7a5SDimitry Andric // Large code model, non-internal function or function whose address 123d65cd7a5SDimitry Andric // was taken, can be accessed through indirect calls. Mark the first 124d65cd7a5SDimitry Andric // BB with ENDBR instruction unless nocf_check attribute is used. 125d65cd7a5SDimitry Andric if ((TM->getCodeModel() == CodeModel::Large || 126d65cd7a5SDimitry Andric MF.getFunction().hasAddressTaken() || 1270b57cec5SDimitry Andric !MF.getFunction().hasLocalLinkage()) && 1280b57cec5SDimitry Andric !MF.getFunction().doesNoCfCheck()) { 1290b57cec5SDimitry Andric auto MBB = MF.begin(); 1300b57cec5SDimitry Andric Changed |= addENDBR(*MBB, MBB->begin()); 1310b57cec5SDimitry Andric } 1320b57cec5SDimitry Andric 1330b57cec5SDimitry Andric for (auto &MBB : MF) { 1340b57cec5SDimitry Andric // Find all basic blocks that their address was taken (for example 1350b57cec5SDimitry Andric // in the case of indirect jump) and add ENDBR instruction. 1360b57cec5SDimitry Andric if (MBB.hasAddressTaken()) 1370b57cec5SDimitry Andric Changed |= addENDBR(MBB, MBB.begin()); 1380b57cec5SDimitry Andric 1390b57cec5SDimitry Andric for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) { 140*0eae32dcSDimitry Andric if (I->isCall() && I->getNumOperands() > 0 && 141*0eae32dcSDimitry Andric IsCallReturnTwice(I->getOperand(0))) { 1420b57cec5SDimitry Andric Changed |= addENDBR(MBB, std::next(I)); 1435ffd83dbSDimitry Andric } 144*0eae32dcSDimitry Andric } 145d65cd7a5SDimitry Andric 1465ffd83dbSDimitry Andric // Exception handle may indirectly jump to catch pad, So we should add 1475ffd83dbSDimitry Andric // ENDBR before catch pad instructions. For SjLj exception model, it will 1485ffd83dbSDimitry Andric // create a new BB(new landingpad) indirectly jump to the old landingpad. 1495ffd83dbSDimitry Andric if (TM->Options.ExceptionModel == ExceptionHandling::SjLj) { 1505ffd83dbSDimitry Andric for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) { 1515ffd83dbSDimitry Andric // New Landingpad BB without EHLabel. 1525ffd83dbSDimitry Andric if (MBB.isEHPad()) { 1535ffd83dbSDimitry Andric if (I->isDebugInstr()) 1545ffd83dbSDimitry Andric continue; 1555ffd83dbSDimitry Andric Changed |= addENDBR(MBB, I); 1565ffd83dbSDimitry Andric break; 1575ffd83dbSDimitry Andric } else if (I->isEHLabel()) { 1585ffd83dbSDimitry Andric // Old Landingpad BB (is not Landingpad now) with 1595ffd83dbSDimitry Andric // the the old "callee" EHLabel. 1605ffd83dbSDimitry Andric MCSymbol *Sym = I->getOperand(0).getMCSymbol(); 1615ffd83dbSDimitry Andric if (!MF.hasCallSiteLandingPad(Sym)) 1625ffd83dbSDimitry Andric continue; 163d65cd7a5SDimitry Andric Changed |= addENDBR(MBB, std::next(I)); 1645ffd83dbSDimitry Andric break; 1655ffd83dbSDimitry Andric } 1665ffd83dbSDimitry Andric } 1675ffd83dbSDimitry Andric } else if (MBB.isEHPad()){ 1685ffd83dbSDimitry Andric for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) { 1695ffd83dbSDimitry Andric if (!I->isEHLabel()) 1705ffd83dbSDimitry Andric continue; 1715ffd83dbSDimitry Andric Changed |= addENDBR(MBB, std::next(I)); 1725ffd83dbSDimitry Andric break; 173d65cd7a5SDimitry Andric } 1740b57cec5SDimitry Andric } 1750b57cec5SDimitry Andric } 1760b57cec5SDimitry Andric return Changed; 1770b57cec5SDimitry Andric } 178