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