File: | lib/Transforms/IPO/CrossDSOCFI.cpp |
Warning: | line 110, column 3 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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-exception | |||
6 | // | |||
7 | //===----------------------------------------------------------------------===// | |||
8 | // | |||
9 | // This pass exports all llvm.bitset's found in the module in the form of a | |||
10 | // __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/ADT/Triple.h" | |||
18 | #include "llvm/IR/Constant.h" | |||
19 | #include "llvm/IR/Constants.h" | |||
20 | #include "llvm/IR/Function.h" | |||
21 | #include "llvm/IR/GlobalObject.h" | |||
22 | #include "llvm/IR/GlobalVariable.h" | |||
23 | #include "llvm/IR/IRBuilder.h" | |||
24 | #include "llvm/IR/Instructions.h" | |||
25 | #include "llvm/IR/Intrinsics.h" | |||
26 | #include "llvm/IR/MDBuilder.h" | |||
27 | #include "llvm/IR/Module.h" | |||
28 | #include "llvm/IR/Operator.h" | |||
29 | #include "llvm/Pass.h" | |||
30 | #include "llvm/Support/Debug.h" | |||
31 | #include "llvm/Support/raw_ostream.h" | |||
32 | #include "llvm/Transforms/IPO.h" | |||
33 | ||||
34 | using namespace llvm; | |||
35 | ||||
36 | #define DEBUG_TYPE"cross-dso-cfi" "cross-dso-cfi" | |||
37 | ||||
38 | STATISTIC(NumTypeIds, "Number of unique type identifiers")static llvm::Statistic NumTypeIds = {"cross-dso-cfi", "NumTypeIds" , "Number of unique type identifiers"}; | |||
39 | ||||
40 | namespace { | |||
41 | ||||
42 | struct CrossDSOCFI : public ModulePass { | |||
43 | static char ID; | |||
44 | CrossDSOCFI() : ModulePass(ID) { | |||
45 | initializeCrossDSOCFIPass(*PassRegistry::getPassRegistry()); | |||
46 | } | |||
47 | ||||
48 | MDNode *VeryLikelyWeights; | |||
49 | ||||
50 | ConstantInt *extractNumericTypeId(MDNode *MD); | |||
51 | void buildCFICheck(Module &M); | |||
52 | bool runOnModule(Module &M) override; | |||
53 | }; | |||
54 | ||||
55 | } // anonymous namespace | |||
56 | ||||
57 | INITIALIZE_PASS_BEGIN(CrossDSOCFI, "cross-dso-cfi", "Cross-DSO CFI", false,static void *initializeCrossDSOCFIPassOnce(PassRegistry & Registry) { | |||
58 | false)static void *initializeCrossDSOCFIPassOnce(PassRegistry & Registry) { | |||
59 | INITIALIZE_PASS_END(CrossDSOCFI, "cross-dso-cfi", "Cross-DSO CFI", false, false)PassInfo *PI = new PassInfo( "Cross-DSO CFI", "cross-dso-cfi" , &CrossDSOCFI::ID, PassInfo::NormalCtor_t(callDefaultCtor <CrossDSOCFI>), false, false); Registry.registerPass(*PI , true); return PI; } static llvm::once_flag InitializeCrossDSOCFIPassFlag ; void llvm::initializeCrossDSOCFIPass(PassRegistry &Registry ) { llvm::call_once(InitializeCrossDSOCFIPassFlag, initializeCrossDSOCFIPassOnce , std::ref(Registry)); } | |||
60 | char CrossDSOCFI::ID = 0; | |||
61 | ||||
62 | ModulePass *llvm::createCrossDSOCFIPass() { return new CrossDSOCFI; } | |||
63 | ||||
64 | /// Extracts a numeric type identifier from an MDNode containing type metadata. | |||
65 | ConstantInt *CrossDSOCFI::extractNumericTypeId(MDNode *MD) { | |||
66 | // This check excludes vtables for classes inside anonymous namespaces. | |||
67 | auto TM = dyn_cast<ValueAsMetadata>(MD->getOperand(1)); | |||
68 | if (!TM) | |||
69 | return nullptr; | |||
70 | auto C = dyn_cast_or_null<ConstantInt>(TM->getValue()); | |||
71 | if (!C) return nullptr; | |||
72 | // We are looking for i64 constants. | |||
73 | if (C->getBitWidth() != 64) return nullptr; | |||
74 | ||||
75 | return C; | |||
76 | } | |||
77 | ||||
78 | /// buildCFICheck - emits __cfi_check for the current module. | |||
79 | void CrossDSOCFI::buildCFICheck(Module &M) { | |||
80 | // FIXME: verify that __cfi_check ends up near the end of the code section, | |||
81 | // but before the jump slots created in LowerTypeTests. | |||
82 | SetVector<uint64_t> TypeIds; | |||
83 | SmallVector<MDNode *, 2> Types; | |||
84 | for (GlobalObject &GO : M.global_objects()) { | |||
85 | Types.clear(); | |||
86 | GO.getMetadata(LLVMContext::MD_type, Types); | |||
87 | for (MDNode *Type : Types) | |||
88 | if (ConstantInt *TypeId = extractNumericTypeId(Type)) | |||
89 | TypeIds.insert(TypeId->getZExtValue()); | |||
90 | } | |||
91 | ||||
92 | NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions"); | |||
93 | if (CfiFunctionsMD) { | |||
94 | for (auto Func : CfiFunctionsMD->operands()) { | |||
95 | assert(Func->getNumOperands() >= 2)((Func->getNumOperands() >= 2) ? static_cast<void> (0) : __assert_fail ("Func->getNumOperands() >= 2", "/build/llvm-toolchain-snapshot-10~svn374877/lib/Transforms/IPO/CrossDSOCFI.cpp" , 95, __PRETTY_FUNCTION__)); | |||
96 | for (unsigned I = 2; I < Func->getNumOperands(); ++I) | |||
97 | if (ConstantInt *TypeId = | |||
98 | extractNumericTypeId(cast<MDNode>(Func->getOperand(I).get()))) | |||
99 | TypeIds.insert(TypeId->getZExtValue()); | |||
100 | } | |||
101 | } | |||
102 | ||||
103 | LLVMContext &Ctx = M.getContext(); | |||
104 | FunctionCallee C = M.getOrInsertFunction( | |||
105 | "__cfi_check", Type::getVoidTy(Ctx), Type::getInt64Ty(Ctx), | |||
106 | Type::getInt8PtrTy(Ctx), Type::getInt8PtrTy(Ctx)); | |||
107 | Function *F = dyn_cast<Function>(C.getCallee()); | |||
108 | // Take over the existing function. The frontend emits a weak stub so that the | |||
109 | // linker knows about the symbol; this pass replaces the function body. | |||
110 | F->deleteBody(); | |||
| ||||
111 | F->setAlignment(4096); | |||
112 | ||||
113 | Triple T(M.getTargetTriple()); | |||
114 | if (T.isARM() || T.isThumb()) | |||
115 | F->addFnAttr("target-features", "+thumb-mode"); | |||
116 | ||||
117 | auto args = F->arg_begin(); | |||
118 | Value &CallSiteTypeId = *(args++); | |||
119 | CallSiteTypeId.setName("CallSiteTypeId"); | |||
120 | Value &Addr = *(args++); | |||
121 | Addr.setName("Addr"); | |||
122 | Value &CFICheckFailData = *(args++); | |||
123 | CFICheckFailData.setName("CFICheckFailData"); | |||
124 | assert(args == F->arg_end())((args == F->arg_end()) ? static_cast<void> (0) : __assert_fail ("args == F->arg_end()", "/build/llvm-toolchain-snapshot-10~svn374877/lib/Transforms/IPO/CrossDSOCFI.cpp" , 124, __PRETTY_FUNCTION__)); | |||
125 | ||||
126 | BasicBlock *BB = BasicBlock::Create(Ctx, "entry", F); | |||
127 | BasicBlock *ExitBB = BasicBlock::Create(Ctx, "exit", F); | |||
128 | ||||
129 | BasicBlock *TrapBB = BasicBlock::Create(Ctx, "fail", F); | |||
130 | IRBuilder<> IRBFail(TrapBB); | |||
131 | FunctionCallee CFICheckFailFn = | |||
132 | M.getOrInsertFunction("__cfi_check_fail", Type::getVoidTy(Ctx), | |||
133 | Type::getInt8PtrTy(Ctx), Type::getInt8PtrTy(Ctx)); | |||
134 | IRBFail.CreateCall(CFICheckFailFn, {&CFICheckFailData, &Addr}); | |||
135 | IRBFail.CreateBr(ExitBB); | |||
136 | ||||
137 | IRBuilder<> IRBExit(ExitBB); | |||
138 | IRBExit.CreateRetVoid(); | |||
139 | ||||
140 | IRBuilder<> IRB(BB); | |||
141 | SwitchInst *SI = IRB.CreateSwitch(&CallSiteTypeId, TrapBB, TypeIds.size()); | |||
142 | for (uint64_t TypeId : TypeIds) { | |||
143 | ConstantInt *CaseTypeId = ConstantInt::get(Type::getInt64Ty(Ctx), TypeId); | |||
144 | BasicBlock *TestBB = BasicBlock::Create(Ctx, "test", F); | |||
145 | IRBuilder<> IRBTest(TestBB); | |||
146 | Function *BitsetTestFn = Intrinsic::getDeclaration(&M, Intrinsic::type_test); | |||
147 | ||||
148 | Value *Test = IRBTest.CreateCall( | |||
149 | BitsetTestFn, {&Addr, MetadataAsValue::get( | |||
150 | Ctx, ConstantAsMetadata::get(CaseTypeId))}); | |||
151 | BranchInst *BI = IRBTest.CreateCondBr(Test, ExitBB, TrapBB); | |||
152 | BI->setMetadata(LLVMContext::MD_prof, VeryLikelyWeights); | |||
153 | ||||
154 | SI->addCase(CaseTypeId, TestBB); | |||
155 | ++NumTypeIds; | |||
156 | } | |||
157 | } | |||
158 | ||||
159 | bool CrossDSOCFI::runOnModule(Module &M) { | |||
160 | VeryLikelyWeights = | |||
161 | MDBuilder(M.getContext()).createBranchWeights((1U << 20) - 1, 1); | |||
162 | if (M.getModuleFlag("Cross-DSO CFI") == nullptr) | |||
163 | return false; | |||
164 | buildCFICheck(M); | |||
165 | return true; | |||
166 | } | |||
167 | ||||
168 | PreservedAnalyses CrossDSOCFIPass::run(Module &M, ModuleAnalysisManager &AM) { | |||
169 | CrossDSOCFI Impl; | |||
170 | bool Changed = Impl.runOnModule(M); | |||
| ||||
171 | if (!Changed) | |||
172 | return PreservedAnalyses::all(); | |||
173 | return PreservedAnalyses::none(); | |||
174 | } |