155 lines · cpp
1//===-- CrossDSOCFI.cpp - Externalize this module's CFI checks ------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This pass exports all llvm.bitset's found in the module in the form of a10// __cfi_check function, which can be used to verify cross-DSO call targets.11//12//===----------------------------------------------------------------------===//13 14#include "llvm/Transforms/IPO/CrossDSOCFI.h"15#include "llvm/ADT/SetVector.h"16#include "llvm/ADT/Statistic.h"17#include "llvm/IR/Constants.h"18#include "llvm/IR/Function.h"19#include "llvm/IR/GlobalObject.h"20#include "llvm/IR/IRBuilder.h"21#include "llvm/IR/Instructions.h"22#include "llvm/IR/Intrinsics.h"23#include "llvm/IR/MDBuilder.h"24#include "llvm/IR/Module.h"25#include "llvm/TargetParser/Triple.h"26 27using namespace llvm;28 29#define DEBUG_TYPE "cross-dso-cfi"30 31STATISTIC(NumTypeIds, "Number of unique type identifiers");32 33namespace {34 35struct CrossDSOCFI {36 MDNode *VeryLikelyWeights;37 38 ConstantInt *extractNumericTypeId(MDNode *MD);39 void buildCFICheck(Module &M);40 bool runOnModule(Module &M);41};42 43} // anonymous namespace44 45/// Extracts a numeric type identifier from an MDNode containing type metadata.46ConstantInt *CrossDSOCFI::extractNumericTypeId(MDNode *MD) {47 // This check excludes vtables for classes inside anonymous namespaces.48 auto TM = dyn_cast<ValueAsMetadata>(MD->getOperand(1));49 if (!TM)50 return nullptr;51 auto C = dyn_cast_or_null<ConstantInt>(TM->getValue());52 if (!C) return nullptr;53 // We are looking for i64 constants.54 if (C->getBitWidth() != 64) return nullptr;55 56 return C;57}58 59/// buildCFICheck - emits __cfi_check for the current module.60void CrossDSOCFI::buildCFICheck(Module &M) {61 // FIXME: verify that __cfi_check ends up near the end of the code section,62 // but before the jump slots created in LowerTypeTests.63 SetVector<uint64_t> TypeIds;64 SmallVector<MDNode *, 2> Types;65 for (GlobalObject &GO : M.global_objects()) {66 Types.clear();67 GO.getMetadata(LLVMContext::MD_type, Types);68 for (MDNode *Type : Types)69 if (ConstantInt *TypeId = extractNumericTypeId(Type))70 TypeIds.insert(TypeId->getZExtValue());71 }72 73 NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions");74 if (CfiFunctionsMD) {75 for (auto *Func : CfiFunctionsMD->operands()) {76 assert(Func->getNumOperands() >= 2);77 for (unsigned I = 2; I < Func->getNumOperands(); ++I)78 if (ConstantInt *TypeId =79 extractNumericTypeId(cast<MDNode>(Func->getOperand(I).get())))80 TypeIds.insert(TypeId->getZExtValue());81 }82 }83 84 LLVMContext &Ctx = M.getContext();85 FunctionCallee C = M.getOrInsertFunction(86 "__cfi_check", Type::getVoidTy(Ctx), Type::getInt64Ty(Ctx),87 PointerType::getUnqual(Ctx), PointerType::getUnqual(Ctx));88 Function *F = cast<Function>(C.getCallee());89 // Take over the existing function. The frontend emits a weak stub so that the90 // linker knows about the symbol; this pass replaces the function body.91 F->deleteBody();92 F->setAlignment(Align(4096));93 94 Triple T(M.getTargetTriple());95 if (T.isARM() || T.isThumb())96 F->addFnAttr("target-features", "+thumb-mode");97 98 auto args = F->arg_begin();99 Value &CallSiteTypeId = *(args++);100 CallSiteTypeId.setName("CallSiteTypeId");101 Value &Addr = *(args++);102 Addr.setName("Addr");103 Value &CFICheckFailData = *(args++);104 CFICheckFailData.setName("CFICheckFailData");105 assert(args == F->arg_end());106 107 BasicBlock *BB = BasicBlock::Create(Ctx, "entry", F);108 BasicBlock *ExitBB = BasicBlock::Create(Ctx, "exit", F);109 110 BasicBlock *TrapBB = BasicBlock::Create(Ctx, "fail", F);111 IRBuilder<> IRBFail(TrapBB);112 FunctionCallee CFICheckFailFn = M.getOrInsertFunction(113 "__cfi_check_fail", Type::getVoidTy(Ctx), PointerType::getUnqual(Ctx),114 PointerType::getUnqual(Ctx));115 IRBFail.CreateCall(CFICheckFailFn, {&CFICheckFailData, &Addr});116 IRBFail.CreateBr(ExitBB);117 118 IRBuilder<> IRBExit(ExitBB);119 IRBExit.CreateRetVoid();120 121 IRBuilder<> IRB(BB);122 SwitchInst *SI = IRB.CreateSwitch(&CallSiteTypeId, TrapBB, TypeIds.size());123 for (uint64_t TypeId : TypeIds) {124 ConstantInt *CaseTypeId = ConstantInt::get(Type::getInt64Ty(Ctx), TypeId);125 BasicBlock *TestBB = BasicBlock::Create(Ctx, "test", F);126 IRBuilder<> IRBTest(TestBB);127 128 Value *Test = IRBTest.CreateIntrinsic(129 Intrinsic::type_test,130 {&Addr,131 MetadataAsValue::get(Ctx, ConstantAsMetadata::get(CaseTypeId))});132 BranchInst *BI = IRBTest.CreateCondBr(Test, ExitBB, TrapBB);133 BI->setMetadata(LLVMContext::MD_prof, VeryLikelyWeights);134 135 SI->addCase(CaseTypeId, TestBB);136 ++NumTypeIds;137 }138}139 140bool CrossDSOCFI::runOnModule(Module &M) {141 VeryLikelyWeights = MDBuilder(M.getContext()).createLikelyBranchWeights();142 if (M.getModuleFlag("Cross-DSO CFI") == nullptr)143 return false;144 buildCFICheck(M);145 return true;146}147 148PreservedAnalyses CrossDSOCFIPass::run(Module &M, ModuleAnalysisManager &AM) {149 CrossDSOCFI Impl;150 bool Changed = Impl.runOnModule(M);151 if (!Changed)152 return PreservedAnalyses::all();153 return PreservedAnalyses::none();154}155