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" 21*d65cd7a5SDimitry 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 310b57cec5SDimitry Andric static 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(); 950b57cec5SDimitry Andric if (Attrs.hasAttribute(AttributeList::FunctionIndex, Attribute::ReturnsTwice)) 960b57cec5SDimitry Andric return true; 970b57cec5SDimitry Andric return false; 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric 1000b57cec5SDimitry Andric bool X86IndirectBranchTrackingPass::runOnMachineFunction(MachineFunction &MF) { 1010b57cec5SDimitry Andric const X86Subtarget &SubTarget = MF.getSubtarget<X86Subtarget>(); 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric // Check that the cf-protection-branch is enabled. 1040b57cec5SDimitry Andric Metadata *isCFProtectionSupported = 1050b57cec5SDimitry Andric MF.getMMI().getModule()->getModuleFlag("cf-protection-branch"); 106*d65cd7a5SDimitry Andric // NB: We need to enable IBT in jitted code if JIT compiler is CET 107*d65cd7a5SDimitry Andric // enabled. 108*d65cd7a5SDimitry Andric const X86TargetMachine *TM = 109*d65cd7a5SDimitry Andric static_cast<const X86TargetMachine *>(&MF.getTarget()); 110*d65cd7a5SDimitry Andric #ifdef __CET__ 111*d65cd7a5SDimitry Andric bool isJITwithCET = TM->isJIT(); 112*d65cd7a5SDimitry Andric #else 113*d65cd7a5SDimitry Andric bool isJITwithCET = false; 114*d65cd7a5SDimitry Andric #endif 115*d65cd7a5SDimitry Andric if (!isCFProtectionSupported && !IndirectBranchTracking && !isJITwithCET) 1160b57cec5SDimitry Andric return false; 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric // True if the current MF was changed and false otherwise. 1190b57cec5SDimitry Andric bool Changed = false; 1200b57cec5SDimitry Andric 1210b57cec5SDimitry Andric TII = SubTarget.getInstrInfo(); 1220b57cec5SDimitry Andric EndbrOpcode = SubTarget.is64Bit() ? X86::ENDBR64 : X86::ENDBR32; 1230b57cec5SDimitry Andric 124*d65cd7a5SDimitry Andric // Large code model, non-internal function or function whose address 125*d65cd7a5SDimitry Andric // was taken, can be accessed through indirect calls. Mark the first 126*d65cd7a5SDimitry Andric // BB with ENDBR instruction unless nocf_check attribute is used. 127*d65cd7a5SDimitry Andric if ((TM->getCodeModel() == CodeModel::Large || 128*d65cd7a5SDimitry Andric MF.getFunction().hasAddressTaken() || 1290b57cec5SDimitry Andric !MF.getFunction().hasLocalLinkage()) && 1300b57cec5SDimitry Andric !MF.getFunction().doesNoCfCheck()) { 1310b57cec5SDimitry Andric auto MBB = MF.begin(); 1320b57cec5SDimitry Andric Changed |= addENDBR(*MBB, MBB->begin()); 1330b57cec5SDimitry Andric } 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric for (auto &MBB : MF) { 1360b57cec5SDimitry Andric // Find all basic blocks that their address was taken (for example 1370b57cec5SDimitry Andric // in the case of indirect jump) and add ENDBR instruction. 1380b57cec5SDimitry Andric if (MBB.hasAddressTaken()) 1390b57cec5SDimitry Andric Changed |= addENDBR(MBB, MBB.begin()); 1400b57cec5SDimitry Andric 141*d65cd7a5SDimitry Andric // Exception handle may indirectly jump to catch pad, So we should add 142*d65cd7a5SDimitry Andric // ENDBR before catch pad instructions. 143*d65cd7a5SDimitry Andric bool EHPadIBTNeeded = MBB.isEHPad(); 144*d65cd7a5SDimitry Andric 1450b57cec5SDimitry Andric for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I) { 146*d65cd7a5SDimitry Andric if (I->isCall() && IsCallReturnTwice(I->getOperand(0))) 1470b57cec5SDimitry Andric Changed |= addENDBR(MBB, std::next(I)); 148*d65cd7a5SDimitry Andric 149*d65cd7a5SDimitry Andric if (EHPadIBTNeeded && I->isEHLabel()) { 150*d65cd7a5SDimitry Andric Changed |= addENDBR(MBB, std::next(I)); 151*d65cd7a5SDimitry Andric EHPadIBTNeeded = false; 152*d65cd7a5SDimitry Andric } 1530b57cec5SDimitry Andric } 1540b57cec5SDimitry Andric } 1550b57cec5SDimitry Andric return Changed; 1560b57cec5SDimitry Andric } 157