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" 26*0fca6ea1SDimitry Andric #include "llvm/IR/Module.h" 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric using namespace llvm; 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric #define DEBUG_TYPE "x86-indirect-branch-tracking" 310b57cec5SDimitry Andric 32e8d8bef9SDimitry Andric cl::opt<bool> IndirectBranchTracking( 330b57cec5SDimitry Andric "x86-indirect-branch-tracking", cl::init(false), cl::Hidden, 340b57cec5SDimitry Andric cl::desc("Enable X86 indirect branch tracking pass.")); 350b57cec5SDimitry Andric 360b57cec5SDimitry Andric STATISTIC(NumEndBranchAdded, "Number of ENDBR instructions added"); 370b57cec5SDimitry Andric 380b57cec5SDimitry Andric namespace { 390b57cec5SDimitry Andric class X86IndirectBranchTrackingPass : public MachineFunctionPass { 400b57cec5SDimitry Andric public: 410b57cec5SDimitry Andric X86IndirectBranchTrackingPass() : MachineFunctionPass(ID) {} 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric StringRef getPassName() const override { 440b57cec5SDimitry Andric return "X86 Indirect Branch Tracking"; 450b57cec5SDimitry Andric } 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric private: 500b57cec5SDimitry Andric static char ID; 510b57cec5SDimitry Andric 520b57cec5SDimitry Andric /// Machine instruction info used throughout the class. 53480093f4SDimitry Andric const X86InstrInfo *TII = nullptr; 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric /// Endbr opcode for the current machine function. 56480093f4SDimitry Andric unsigned int EndbrOpcode = 0; 570b57cec5SDimitry Andric 58480093f4SDimitry Andric /// Adds a new ENDBR instruction to the beginning of the MBB. 590b57cec5SDimitry Andric /// The function will not add it if already exists. 600b57cec5SDimitry Andric /// It will add ENDBR32 or ENDBR64 opcode, depending on the target. 610b57cec5SDimitry Andric /// \returns true if the ENDBR was added and false otherwise. 620b57cec5SDimitry Andric bool addENDBR(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const; 630b57cec5SDimitry Andric }; 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric } // end anonymous namespace 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric char X86IndirectBranchTrackingPass::ID = 0; 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric FunctionPass *llvm::createX86IndirectBranchTrackingPass() { 700b57cec5SDimitry Andric return new X86IndirectBranchTrackingPass(); 710b57cec5SDimitry Andric } 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric bool X86IndirectBranchTrackingPass::addENDBR( 740b57cec5SDimitry Andric MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const { 750b57cec5SDimitry Andric assert(TII && "Target instruction info was not initialized"); 760b57cec5SDimitry Andric assert((X86::ENDBR64 == EndbrOpcode || X86::ENDBR32 == EndbrOpcode) && 770b57cec5SDimitry Andric "Unexpected Endbr opcode"); 780b57cec5SDimitry Andric 790b57cec5SDimitry Andric // If the MBB/I is empty or the current instruction is not ENDBR, 800b57cec5SDimitry Andric // insert ENDBR instruction to the location of I. 810b57cec5SDimitry Andric if (I == MBB.end() || I->getOpcode() != EndbrOpcode) { 820b57cec5SDimitry Andric BuildMI(MBB, I, MBB.findDebugLoc(I), TII->get(EndbrOpcode)); 830b57cec5SDimitry Andric ++NumEndBranchAdded; 840b57cec5SDimitry Andric return true; 850b57cec5SDimitry Andric } 860b57cec5SDimitry Andric return false; 870b57cec5SDimitry Andric } 880b57cec5SDimitry Andric 898bcb0991SDimitry Andric static bool IsCallReturnTwice(llvm::MachineOperand &MOp) { 900b57cec5SDimitry Andric if (!MOp.isGlobal()) 910b57cec5SDimitry Andric return false; 920b57cec5SDimitry Andric auto *CalleeFn = dyn_cast<Function>(MOp.getGlobal()); 930b57cec5SDimitry Andric if (!CalleeFn) 940b57cec5SDimitry Andric return false; 950b57cec5SDimitry Andric AttributeList Attrs = CalleeFn->getAttributes(); 96349cc55cSDimitry Andric return Attrs.hasFnAttr(Attribute::ReturnsTwice); 970b57cec5SDimitry Andric } 980b57cec5SDimitry Andric 9904eeddc0SDimitry Andric // Checks if function should have an ENDBR in its prologue 10004eeddc0SDimitry Andric static bool needsPrologueENDBR(MachineFunction &MF, const Module *M) { 10104eeddc0SDimitry Andric Function &F = MF.getFunction(); 10204eeddc0SDimitry Andric 10304eeddc0SDimitry Andric if (F.doesNoCfCheck()) 10404eeddc0SDimitry Andric return false; 10504eeddc0SDimitry Andric 106bdd1243dSDimitry Andric switch (MF.getTarget().getCodeModel()) { 10704eeddc0SDimitry Andric // Large code model functions always reachable through indirect calls. 10804eeddc0SDimitry Andric case CodeModel::Large: 10904eeddc0SDimitry Andric return true; 11004eeddc0SDimitry Andric // Address taken or externally linked functions may be reachable. 11104eeddc0SDimitry Andric default: 11204eeddc0SDimitry Andric return (F.hasAddressTaken() || !F.hasLocalLinkage()); 11304eeddc0SDimitry Andric } 11404eeddc0SDimitry Andric } 11504eeddc0SDimitry Andric 1160b57cec5SDimitry Andric bool X86IndirectBranchTrackingPass::runOnMachineFunction(MachineFunction &MF) { 1170b57cec5SDimitry Andric const X86Subtarget &SubTarget = MF.getSubtarget<X86Subtarget>(); 1180b57cec5SDimitry Andric 119*0fca6ea1SDimitry Andric const Module *M = MF.getFunction().getParent(); 1200b57cec5SDimitry Andric // Check that the cf-protection-branch is enabled. 12104eeddc0SDimitry Andric Metadata *isCFProtectionSupported = M->getModuleFlag("cf-protection-branch"); 12204eeddc0SDimitry Andric 123d65cd7a5SDimitry Andric // NB: We need to enable IBT in jitted code if JIT compiler is CET 124d65cd7a5SDimitry Andric // enabled. 125d65cd7a5SDimitry Andric const X86TargetMachine *TM = 126d65cd7a5SDimitry Andric static_cast<const X86TargetMachine *>(&MF.getTarget()); 127d65cd7a5SDimitry Andric #ifdef __CET__ 128d65cd7a5SDimitry Andric bool isJITwithCET = TM->isJIT(); 129d65cd7a5SDimitry Andric #else 130d65cd7a5SDimitry Andric bool isJITwithCET = false; 131d65cd7a5SDimitry Andric #endif 132d65cd7a5SDimitry Andric if (!isCFProtectionSupported && !IndirectBranchTracking && !isJITwithCET) 1330b57cec5SDimitry Andric return false; 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric // True if the current MF was changed and false otherwise. 1360b57cec5SDimitry Andric bool Changed = false; 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric TII = SubTarget.getInstrInfo(); 1390b57cec5SDimitry Andric EndbrOpcode = SubTarget.is64Bit() ? X86::ENDBR64 : X86::ENDBR32; 1400b57cec5SDimitry Andric 14104eeddc0SDimitry Andric // If function is reachable indirectly, mark the first BB with ENDBR. 14204eeddc0SDimitry Andric if (needsPrologueENDBR(MF, M)) { 1430b57cec5SDimitry Andric auto MBB = MF.begin(); 1440b57cec5SDimitry Andric Changed |= addENDBR(*MBB, MBB->begin()); 1450b57cec5SDimitry Andric } 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric for (auto &MBB : MF) { 1480b57cec5SDimitry Andric // Find all basic blocks that their address was taken (for example 1490b57cec5SDimitry Andric // in the case of indirect jump) and add ENDBR instruction. 1500b57cec5SDimitry Andric if (MBB.hasAddressTaken()) 1510b57cec5SDimitry Andric Changed |= addENDBR(MBB, MBB.begin()); 1520b57cec5SDimitry Andric 1530b57cec5SDimitry Andric for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) { 1540eae32dcSDimitry Andric if (I->isCall() && I->getNumOperands() > 0 && 1550eae32dcSDimitry Andric IsCallReturnTwice(I->getOperand(0))) { 1560b57cec5SDimitry Andric Changed |= addENDBR(MBB, std::next(I)); 1575ffd83dbSDimitry Andric } 1580eae32dcSDimitry Andric } 159d65cd7a5SDimitry Andric 1605ffd83dbSDimitry Andric // Exception handle may indirectly jump to catch pad, So we should add 1615ffd83dbSDimitry Andric // ENDBR before catch pad instructions. For SjLj exception model, it will 1625ffd83dbSDimitry Andric // create a new BB(new landingpad) indirectly jump to the old landingpad. 1635ffd83dbSDimitry Andric if (TM->Options.ExceptionModel == ExceptionHandling::SjLj) { 1645ffd83dbSDimitry Andric for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) { 1655ffd83dbSDimitry Andric // New Landingpad BB without EHLabel. 1665ffd83dbSDimitry Andric if (MBB.isEHPad()) { 1675ffd83dbSDimitry Andric if (I->isDebugInstr()) 1685ffd83dbSDimitry Andric continue; 1695ffd83dbSDimitry Andric Changed |= addENDBR(MBB, I); 1705ffd83dbSDimitry Andric break; 1715ffd83dbSDimitry Andric } else if (I->isEHLabel()) { 1725ffd83dbSDimitry Andric // Old Landingpad BB (is not Landingpad now) with 1735f757f3fSDimitry Andric // the old "callee" EHLabel. 1745ffd83dbSDimitry Andric MCSymbol *Sym = I->getOperand(0).getMCSymbol(); 1755ffd83dbSDimitry Andric if (!MF.hasCallSiteLandingPad(Sym)) 1765ffd83dbSDimitry Andric continue; 177d65cd7a5SDimitry Andric Changed |= addENDBR(MBB, std::next(I)); 1785ffd83dbSDimitry Andric break; 1795ffd83dbSDimitry Andric } 1805ffd83dbSDimitry Andric } 1815ffd83dbSDimitry Andric } else if (MBB.isEHPad()){ 1825ffd83dbSDimitry Andric for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) { 1835ffd83dbSDimitry Andric if (!I->isEHLabel()) 1845ffd83dbSDimitry Andric continue; 1855ffd83dbSDimitry Andric Changed |= addENDBR(MBB, std::next(I)); 1865ffd83dbSDimitry Andric break; 187d65cd7a5SDimitry Andric } 1880b57cec5SDimitry Andric } 1890b57cec5SDimitry Andric } 1900b57cec5SDimitry Andric return Changed; 1910b57cec5SDimitry Andric } 192