diff options
| author | Peter Collingbourne <peter@pcc.me.uk> | 2015-02-20 20:30:56 +0000 |
|---|---|---|
| committer | Peter Collingbourne <peter@pcc.me.uk> | 2015-02-20 20:30:56 +0000 |
| commit | a4ccff32818c05c9f2d7a2a6503866d13636b664 (patch) | |
| tree | d0853e78880e850956141ea043fecb36250e5842 /clang/lib/CodeGen | |
| parent | e6909c8e8ba07acb5e6366186fe186c91054e93c (diff) | |
| download | bcm5719-llvm-a4ccff32818c05c9f2d7a2a6503866d13636b664.tar.gz bcm5719-llvm-a4ccff32818c05c9f2d7a2a6503866d13636b664.zip | |
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
Diffstat (limited to 'clang/lib/CodeGen')
| -rw-r--r-- | clang/lib/CodeGen/CGClass.cpp | 33 | ||||
| -rw-r--r-- | clang/lib/CodeGen/CGVTables.cpp | 61 | ||||
| -rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.h | 3 | ||||
| -rw-r--r-- | clang/lib/CodeGen/CodeGenModule.h | 5 | ||||
| -rw-r--r-- | clang/lib/CodeGen/ItaniumCXXABI.cpp | 4 |
5 files changed, 106 insertions, 0 deletions
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 524697cd230..66ab6152c21 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -24,6 +24,7 @@ #include "clang/Basic/TargetBuiltins.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/Frontend/CodeGenOptions.h" +#include "llvm/IR/Intrinsics.h" using namespace clang; using namespace CodeGen; @@ -2087,6 +2088,38 @@ llvm::Value *CodeGenFunction::GetVTablePtr(llvm::Value *This, return VTable; } +void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, + llvm::Value *VTable) { + if (!SanOpts.has(SanitizerKind::CFIVptr)) + return; + + const CXXRecordDecl *RD = MD->getParent(); + // FIXME: Add blacklisting scheme. + if (RD->isInStdNamespace()) + return; + + std::string OutName; + llvm::raw_string_ostream Out(OutName); + CGM.getCXXABI().getMangleContext().mangleCXXVTableBitSet(RD, Out); + + llvm::Value *BitSetName = llvm::MetadataAsValue::get( + getLLVMContext(), llvm::MDString::get(getLLVMContext(), Out.str())); + + llvm::Value *BitSetTest = Builder.CreateCall2( + CGM.getIntrinsic(llvm::Intrinsic::bitset_test), + Builder.CreateBitCast(VTable, CGM.Int8PtrTy), BitSetName); + + llvm::BasicBlock *ContBlock = createBasicBlock("vtable.check.cont"); + llvm::BasicBlock *TrapBlock = createBasicBlock("vtable.check.trap"); + + Builder.CreateCondBr(BitSetTest, ContBlock, TrapBlock); + + EmitBlock(TrapBlock); + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::trap)); + Builder.CreateUnreachable(); + + EmitBlock(ContBlock); +} // FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do // quite what we want. diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp index 77244677650..14116e498ee 100644 --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -669,6 +669,8 @@ CodeGenVTables::GenerateConstructionVTable(const CXXRecordDecl *RD, VTLayout->getNumVTableThunks(), RTTI); VTable->setInitializer(Init); + CGM.EmitVTableBitSetEntries(VTable, *VTLayout.get()); + return VTable; } @@ -837,3 +839,62 @@ void CodeGenModule::EmitDeferredVTables() { "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); +} diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index b52ba8ab964..7571332af06 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1346,6 +1346,9 @@ public: /// to by This. llvm::Value *GetVTablePtr(llvm::Value *This, llvm::Type *Ty); + /// EmitVTablePtrCheckForCall - Virtual method MD is being called via VTable. + /// If vptr CFI is enabled, emit a check that VTable is valid. + void EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, llvm::Value *VTable); /// CanDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given /// expr can be devirtualized. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 7daa0932e1a..6902d1917a6 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1103,6 +1103,11 @@ public: /// \param D Threadprivate declaration. void EmitOMPThreadPrivateDecl(const OMPThreadPrivateDecl *D); + /// Emit bit set entries for the given vtable using the given layout if + /// vptr CFI is enabled. + void EmitVTableBitSetEntries(llvm::GlobalVariable *VTable, + const VTableLayout &VTLayout); + private: llvm::Constant * GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D, diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 14a2890b29c..e580969ce5b 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -1281,6 +1281,8 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, cast<NamespaceDecl>(DC)->getIdentifier()->isStr("__cxxabiv1") && DC->getParent()->isTranslationUnit()) EmitFundamentalRTTIDescriptors(); + + CGM.EmitVTableBitSetEntries(VTable, VTLayout); } llvm::Value *ItaniumCXXABI::getVTableAddressPointInStructor( @@ -1372,6 +1374,8 @@ llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, Ty = Ty->getPointerTo()->getPointerTo(); llvm::Value *VTable = CGF.GetVTablePtr(This, Ty); + CGF.EmitVTablePtrCheckForCall(cast<CXXMethodDecl>(GD.getDecl()), VTable); + uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); llvm::Value *VFuncPtr = CGF.Builder.CreateConstInBoundsGEP1_64(VTable, VTableIndex, "vfn"); |

