1 //===-- KCFI.cpp - Generic KCFI operand bundle lowering ---------*- C++ -*-===// 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 // 9 // This pass emits generic KCFI indirect call checks for targets that don't 10 // support lowering KCFI operand bundles in the back-end. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Transforms/Instrumentation/KCFI.h" 15 #include "llvm/ADT/Statistic.h" 16 #include "llvm/IR/Constants.h" 17 #include "llvm/IR/DiagnosticInfo.h" 18 #include "llvm/IR/DiagnosticPrinter.h" 19 #include "llvm/IR/Function.h" 20 #include "llvm/IR/IRBuilder.h" 21 #include "llvm/IR/InstIterator.h" 22 #include "llvm/IR/Instructions.h" 23 #include "llvm/IR/Intrinsics.h" 24 #include "llvm/IR/MDBuilder.h" 25 #include "llvm/IR/Module.h" 26 #include "llvm/Target/TargetMachine.h" 27 #include "llvm/Transforms/Utils/BasicBlockUtils.h" 28 29 using namespace llvm; 30 31 #define DEBUG_TYPE "kcfi" 32 33 STATISTIC(NumKCFIChecks, "Number of kcfi operands transformed into checks"); 34 35 namespace { 36 class DiagnosticInfoKCFI : public DiagnosticInfo { 37 const Twine &Msg; 38 39 public: 40 DiagnosticInfoKCFI(const Twine &DiagMsg, 41 DiagnosticSeverity Severity = DS_Error) 42 : DiagnosticInfo(DK_Linker, Severity), Msg(DiagMsg) {} 43 void print(DiagnosticPrinter &DP) const override { DP << Msg; } 44 }; 45 } // namespace 46 47 PreservedAnalyses KCFIPass::run(Function &F, FunctionAnalysisManager &AM) { 48 Module &M = *F.getParent(); 49 if (!M.getModuleFlag("kcfi")) 50 return PreservedAnalyses::all(); 51 52 // Find call instructions with KCFI operand bundles. 53 SmallVector<CallInst *> KCFICalls; 54 for (Instruction &I : instructions(F)) { 55 if (auto *CI = dyn_cast<CallInst>(&I)) 56 if (CI->getOperandBundle(LLVMContext::OB_kcfi)) 57 KCFICalls.push_back(CI); 58 } 59 60 if (KCFICalls.empty()) 61 return PreservedAnalyses::all(); 62 63 LLVMContext &Ctx = M.getContext(); 64 // patchable-function-prefix emits nops between the KCFI type identifier 65 // and the function start. As we don't know the size of the emitted nops, 66 // don't allow this attribute with generic lowering. 67 if (F.hasFnAttribute("patchable-function-prefix")) 68 Ctx.diagnose( 69 DiagnosticInfoKCFI("-fpatchable-function-entry=N,M, where M>0 is not " 70 "compatible with -fsanitize=kcfi on this target")); 71 72 IntegerType *Int32Ty = Type::getInt32Ty(Ctx); 73 MDNode *VeryUnlikelyWeights = MDBuilder(Ctx).createUnlikelyBranchWeights(); 74 Triple T(M.getTargetTriple()); 75 76 for (CallInst *CI : KCFICalls) { 77 // Get the expected hash value. 78 const uint32_t ExpectedHash = 79 cast<ConstantInt>(CI->getOperandBundle(LLVMContext::OB_kcfi)->Inputs[0]) 80 ->getZExtValue(); 81 82 // Drop the KCFI operand bundle. 83 CallBase *Call = CallBase::removeOperandBundle(CI, LLVMContext::OB_kcfi, 84 CI->getIterator()); 85 assert(Call != CI); 86 Call->copyMetadata(*CI); 87 CI->replaceAllUsesWith(Call); 88 CI->eraseFromParent(); 89 90 if (!Call->isIndirectCall()) 91 continue; 92 93 // Emit a check and trap if the target hash doesn't match. 94 IRBuilder<> Builder(Call); 95 Value *FuncPtr = Call->getCalledOperand(); 96 // ARM uses the least significant bit of the function pointer to select 97 // between ARM and Thumb modes for the callee. Instructions are always 98 // at least 16-bit aligned, so clear the LSB before we compute the hash 99 // location. 100 if (T.isARM() || T.isThumb()) { 101 FuncPtr = Builder.CreateIntToPtr( 102 Builder.CreateAnd(Builder.CreatePtrToInt(FuncPtr, Int32Ty), 103 ConstantInt::get(Int32Ty, -2)), 104 FuncPtr->getType()); 105 } 106 Value *HashPtr = Builder.CreateConstInBoundsGEP1_32(Int32Ty, FuncPtr, -1); 107 Value *Test = Builder.CreateICmpNE(Builder.CreateLoad(Int32Ty, HashPtr), 108 ConstantInt::get(Int32Ty, ExpectedHash)); 109 Instruction *ThenTerm = 110 SplitBlockAndInsertIfThen(Test, Call, false, VeryUnlikelyWeights); 111 Builder.SetInsertPoint(ThenTerm); 112 Builder.CreateIntrinsic(Intrinsic::debugtrap, {}, {}); 113 ++NumKCFIChecks; 114 } 115 116 return PreservedAnalyses::none(); 117 } 118