/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/lib/Transforms/IPO/CrossDSOCFI.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- CrossDSOCFI.cpp - Externalize this module's CFI checks ------------===// |
2 | | // |
3 | | // The LLVM Compiler Infrastructure |
4 | | // |
5 | | // This file is distributed under the University of Illinois Open Source |
6 | | // License. See LICENSE.TXT for details. |
7 | | // |
8 | | //===----------------------------------------------------------------------===// |
9 | | // |
10 | | // This pass exports all llvm.bitset's found in the module in the form of a |
11 | | // __cfi_check function, which can be used to verify cross-DSO call targets. |
12 | | // |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #include "llvm/Transforms/IPO/CrossDSOCFI.h" |
16 | | #include "llvm/ADT/EquivalenceClasses.h" |
17 | | #include "llvm/ADT/SetVector.h" |
18 | | #include "llvm/ADT/Statistic.h" |
19 | | #include "llvm/ADT/Triple.h" |
20 | | #include "llvm/IR/Constant.h" |
21 | | #include "llvm/IR/Constants.h" |
22 | | #include "llvm/IR/Function.h" |
23 | | #include "llvm/IR/GlobalObject.h" |
24 | | #include "llvm/IR/GlobalVariable.h" |
25 | | #include "llvm/IR/IRBuilder.h" |
26 | | #include "llvm/IR/Instructions.h" |
27 | | #include "llvm/IR/Intrinsics.h" |
28 | | #include "llvm/IR/MDBuilder.h" |
29 | | #include "llvm/IR/Module.h" |
30 | | #include "llvm/IR/Operator.h" |
31 | | #include "llvm/Pass.h" |
32 | | #include "llvm/Support/Debug.h" |
33 | | #include "llvm/Support/raw_ostream.h" |
34 | | #include "llvm/Transforms/IPO.h" |
35 | | #include "llvm/Transforms/Utils/BasicBlockUtils.h" |
36 | | |
37 | | using namespace llvm; |
38 | | |
39 | | #define DEBUG_TYPE "cross-dso-cfi" |
40 | | |
41 | | STATISTIC(NumTypeIds, "Number of unique type identifiers"); |
42 | | |
43 | | namespace { |
44 | | |
45 | | struct CrossDSOCFI : public ModulePass { |
46 | | static char ID; |
47 | 229 | CrossDSOCFI() : ModulePass(ID) { |
48 | 229 | initializeCrossDSOCFIPass(*PassRegistry::getPassRegistry()); |
49 | 229 | } |
50 | | |
51 | | MDNode *VeryLikelyWeights; |
52 | | |
53 | | ConstantInt *extractNumericTypeId(MDNode *MD); |
54 | | void buildCFICheck(Module &M); |
55 | | bool runOnModule(Module &M) override; |
56 | | }; |
57 | | |
58 | | } // anonymous namespace |
59 | | |
60 | 8.13k | INITIALIZE_PASS_BEGIN8.13k (CrossDSOCFI, "cross-dso-cfi", "Cross-DSO CFI", false,
|
61 | 8.13k | false) |
62 | 8.13k | INITIALIZE_PASS_END(CrossDSOCFI, "cross-dso-cfi", "Cross-DSO CFI", false, false) |
63 | | char CrossDSOCFI::ID = 0; |
64 | | |
65 | 214 | ModulePass *llvm::createCrossDSOCFIPass() { return new CrossDSOCFI; } |
66 | | |
67 | | /// Extracts a numeric type identifier from an MDNode containing type metadata. |
68 | 48 | ConstantInt *CrossDSOCFI::extractNumericTypeId(MDNode *MD) { |
69 | 48 | // This check excludes vtables for classes inside anonymous namespaces. |
70 | 48 | auto TM = dyn_cast<ValueAsMetadata>(MD->getOperand(1)); |
71 | 48 | if (!TM) |
72 | 24 | return nullptr; |
73 | 24 | auto C = dyn_cast_or_null<ConstantInt>(TM->getValue()); |
74 | 24 | if (!C24 ) return nullptr0 ; |
75 | 24 | // We are looking for i64 constants. |
76 | 24 | if (24 C->getBitWidth() != 6424 ) return nullptr0 ; |
77 | 24 | |
78 | 24 | return C; |
79 | 24 | } |
80 | | |
81 | | /// buildCFICheck - emits __cfi_check for the current module. |
82 | 8 | void CrossDSOCFI::buildCFICheck(Module &M) { |
83 | 8 | // FIXME: verify that __cfi_check ends up near the end of the code section, |
84 | 8 | // but before the jump slots created in LowerTypeTests. |
85 | 8 | SetVector<uint64_t> TypeIds; |
86 | 8 | SmallVector<MDNode *, 2> Types; |
87 | 20 | for (GlobalObject &GO : M.global_objects()) { |
88 | 20 | Types.clear(); |
89 | 20 | GO.getMetadata(LLVMContext::MD_type, Types); |
90 | 40 | for (MDNode *Type : Types) { |
91 | 40 | // Sanity check. GO must not be a function declaration. |
92 | 40 | assert(!isa<Function>(&GO) || !cast<Function>(&GO)->isDeclaration()); |
93 | 40 | |
94 | 40 | if (ConstantInt *TypeId = extractNumericTypeId(Type)) |
95 | 20 | TypeIds.insert(TypeId->getZExtValue()); |
96 | 40 | } |
97 | 20 | } |
98 | 8 | |
99 | 8 | NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions"); |
100 | 8 | if (CfiFunctionsMD8 ) { |
101 | 4 | for (auto Func : CfiFunctionsMD->operands()) { |
102 | 4 | assert(Func->getNumOperands() >= 2); |
103 | 12 | for (unsigned I = 2; I < Func->getNumOperands()12 ; ++I8 ) |
104 | 8 | if (ConstantInt *8 TypeId8 = |
105 | 8 | extractNumericTypeId(cast<MDNode>(Func->getOperand(I).get()))) |
106 | 4 | TypeIds.insert(TypeId->getZExtValue()); |
107 | 4 | } |
108 | 2 | } |
109 | 8 | |
110 | 8 | LLVMContext &Ctx = M.getContext(); |
111 | 8 | Constant *C = M.getOrInsertFunction( |
112 | 8 | "__cfi_check", Type::getVoidTy(Ctx), Type::getInt64Ty(Ctx), |
113 | 8 | Type::getInt8PtrTy(Ctx), Type::getInt8PtrTy(Ctx)); |
114 | 8 | Function *F = dyn_cast<Function>(C); |
115 | 8 | // Take over the existing function. The frontend emits a weak stub so that the |
116 | 8 | // linker knows about the symbol; this pass replaces the function body. |
117 | 8 | F->deleteBody(); |
118 | 8 | F->setAlignment(4096); |
119 | 8 | |
120 | 8 | Triple T(M.getTargetTriple()); |
121 | 8 | if (T.isARM() || 8 T.isThumb()7 ) |
122 | 2 | F->addFnAttr("target-features", "+thumb-mode"); |
123 | 8 | |
124 | 8 | auto args = F->arg_begin(); |
125 | 8 | Value &CallSiteTypeId = *(args++); |
126 | 8 | CallSiteTypeId.setName("CallSiteTypeId"); |
127 | 8 | Value &Addr = *(args++); |
128 | 8 | Addr.setName("Addr"); |
129 | 8 | Value &CFICheckFailData = *(args++); |
130 | 8 | CFICheckFailData.setName("CFICheckFailData"); |
131 | 8 | assert(args == F->arg_end()); |
132 | 8 | |
133 | 8 | BasicBlock *BB = BasicBlock::Create(Ctx, "entry", F); |
134 | 8 | BasicBlock *ExitBB = BasicBlock::Create(Ctx, "exit", F); |
135 | 8 | |
136 | 8 | BasicBlock *TrapBB = BasicBlock::Create(Ctx, "fail", F); |
137 | 8 | IRBuilder<> IRBFail(TrapBB); |
138 | 8 | Constant *CFICheckFailFn = M.getOrInsertFunction( |
139 | 8 | "__cfi_check_fail", Type::getVoidTy(Ctx), Type::getInt8PtrTy(Ctx), |
140 | 8 | Type::getInt8PtrTy(Ctx)); |
141 | 8 | IRBFail.CreateCall(CFICheckFailFn, {&CFICheckFailData, &Addr}); |
142 | 8 | IRBFail.CreateBr(ExitBB); |
143 | 8 | |
144 | 8 | IRBuilder<> IRBExit(ExitBB); |
145 | 8 | IRBExit.CreateRetVoid(); |
146 | 8 | |
147 | 8 | IRBuilder<> IRB(BB); |
148 | 8 | SwitchInst *SI = IRB.CreateSwitch(&CallSiteTypeId, TrapBB, TypeIds.size()); |
149 | 16 | for (uint64_t TypeId : TypeIds) { |
150 | 16 | ConstantInt *CaseTypeId = ConstantInt::get(Type::getInt64Ty(Ctx), TypeId); |
151 | 16 | BasicBlock *TestBB = BasicBlock::Create(Ctx, "test", F); |
152 | 16 | IRBuilder<> IRBTest(TestBB); |
153 | 16 | Function *BitsetTestFn = Intrinsic::getDeclaration(&M, Intrinsic::type_test); |
154 | 16 | |
155 | 16 | Value *Test = IRBTest.CreateCall( |
156 | 16 | BitsetTestFn, {&Addr, MetadataAsValue::get( |
157 | 16 | Ctx, ConstantAsMetadata::get(CaseTypeId))}); |
158 | 16 | BranchInst *BI = IRBTest.CreateCondBr(Test, ExitBB, TrapBB); |
159 | 16 | BI->setMetadata(LLVMContext::MD_prof, VeryLikelyWeights); |
160 | 16 | |
161 | 16 | SI->addCase(CaseTypeId, TestBB); |
162 | 16 | ++NumTypeIds; |
163 | 16 | } |
164 | 8 | } |
165 | | |
166 | 229 | bool CrossDSOCFI::runOnModule(Module &M) { |
167 | 229 | if (skipModule(M)) |
168 | 0 | return false; |
169 | 229 | |
170 | 229 | VeryLikelyWeights = |
171 | 229 | MDBuilder(M.getContext()).createBranchWeights((1U << 20) - 1, 1); |
172 | 229 | if (M.getModuleFlag("Cross-DSO CFI") == nullptr) |
173 | 221 | return false; |
174 | 8 | buildCFICheck(M); |
175 | 8 | return true; |
176 | 8 | } |
177 | | |
178 | 9 | PreservedAnalyses CrossDSOCFIPass::run(Module &M, ModuleAnalysisManager &AM) { |
179 | 9 | CrossDSOCFI Impl; |
180 | 9 | bool Changed = Impl.runOnModule(M); |
181 | 9 | if (!Changed) |
182 | 7 | return PreservedAnalyses::all(); |
183 | 2 | return PreservedAnalyses::none(); |
184 | 2 | } |