Implement Control Flow Integrity for virtual calls.
This patch introduces the -fsanitize=cfi-vptr flag, which enables a control
flow integrity scheme that checks that virtual calls take place using a vptr of
the correct dynamic type. More details in the new docs/ControlFlowIntegrity.rst
file.
It also introduces the -fsanitize=cfi flag, which is currently a synonym for
-fsanitize=cfi-vptr, but will eventually cover all CFI checks implemented
in Clang.
Differential Revision: http://reviews.llvm.org/D7424
llvm-svn: 230055
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 7724467..14116e4 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -669,6 +669,8 @@
VTLayout->getNumVTableThunks(), RTTI);
VTable->setInitializer(Init);
+ CGM.EmitVTableBitSetEntries(VTable, *VTLayout.get());
+
return VTable;
}
@@ -837,3 +839,62 @@
"deferred extra v-tables during v-table emission?");
DeferredVTables.clear();
}
+
+void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
+ const VTableLayout &VTLayout) {
+ if (!LangOpts.Sanitize.has(SanitizerKind::CFIVptr))
+ return;
+
+ llvm::Metadata *VTableMD = llvm::ConstantAsMetadata::get(VTable);
+
+ std::vector<llvm::MDTuple *> BitsetEntries;
+ // Create a bit set entry for each address point.
+ for (auto &&AP : VTLayout.getAddressPoints()) {
+ // FIXME: Add blacklisting scheme.
+ if (AP.first.getBase()->isInStdNamespace())
+ continue;
+
+ std::string OutName;
+ llvm::raw_string_ostream Out(OutName);
+ getCXXABI().getMangleContext().mangleCXXVTableBitSet(AP.first.getBase(),
+ Out);
+
+ CharUnits PointerWidth =
+ Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
+ uint64_t AddrPointOffset = AP.second * PointerWidth.getQuantity();
+
+ llvm::Metadata *BitsetOps[] = {
+ llvm::MDString::get(getLLVMContext(), Out.str()),
+ VTableMD,
+ llvm::ConstantAsMetadata::get(
+ llvm::ConstantInt::get(Int64Ty, AddrPointOffset))};
+ llvm::MDTuple *BitsetEntry =
+ llvm::MDTuple::get(getLLVMContext(), BitsetOps);
+ BitsetEntries.push_back(BitsetEntry);
+ }
+
+ // Sort the bit set entries for determinism.
+ std::sort(BitsetEntries.begin(), BitsetEntries.end(), [](llvm::MDTuple *T1,
+ llvm::MDTuple *T2) {
+ StringRef S1 = cast<llvm::MDString>(T1->getOperand(0))->getString();
+ StringRef S2 = cast<llvm::MDString>(T2->getOperand(0))->getString();
+ if (S1 < S2)
+ return true;
+ if (S1 != S2)
+ return false;
+
+ uint64_t Offset1 = cast<llvm::ConstantInt>(
+ cast<llvm::ConstantAsMetadata>(T1->getOperand(2))
+ ->getValue())->getZExtValue();
+ uint64_t Offset2 = cast<llvm::ConstantInt>(
+ cast<llvm::ConstantAsMetadata>(T2->getOperand(2))
+ ->getValue())->getZExtValue();
+ assert(Offset1 != Offset2);
+ return Offset1 < Offset2;
+ });
+
+ llvm::NamedMDNode *BitsetsMD =
+ getModule().getOrInsertNamedMetadata("llvm.bitsets");
+ for (auto BitsetEntry : BitsetEntries)
+ BitsetsMD->addOperand(BitsetEntry);
+}