summaryrefslogtreecommitdiffstats
path: root/clang/lib/CodeGen/CGClass.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/CodeGen/CGClass.cpp')
-rw-r--r--clang/lib/CodeGen/CGClass.cpp91
1 files changed, 90 insertions, 1 deletions
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 5649708d780..84d6437abbe 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -2093,7 +2093,96 @@ void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD,
if (!SanOpts.has(SanitizerKind::CFIVptr))
return;
- const CXXRecordDecl *RD = MD->getParent();
+ EmitVTablePtrCheck(MD->getParent(), VTable);
+}
+
+// If a class has a single non-virtual base and does not introduce or override
+// virtual member functions or fields, it will have the same layout as its base.
+// This function returns the least derived such class.
+//
+// Casting an instance of a base class to such a derived class is technically
+// undefined behavior, but it is a relatively common hack for introducing member
+// functions on class instances with specific properties (e.g. llvm::Operator)
+// that works under most compilers and should not have security implications, so
+// we allow it by default. It can be disabled with -fsanitize=cfi-cast-strict.
+static const CXXRecordDecl *
+LeastDerivedClassWithSameLayout(const CXXRecordDecl *RD) {
+ if (!RD->field_empty())
+ return RD;
+
+ if (RD->getNumVBases() != 0)
+ return RD;
+
+ if (RD->getNumBases() != 1)
+ return RD;
+
+ for (const CXXMethodDecl *MD : RD->methods()) {
+ if (MD->isVirtual()) {
+ // Virtual member functions are only ok if they are implicit destructors
+ // because the implicit destructor will have the same semantics as the
+ // base class's destructor if no fields are added.
+ if (isa<CXXDestructorDecl>(MD) && MD->isImplicit())
+ continue;
+ return RD;
+ }
+ }
+
+ return LeastDerivedClassWithSameLayout(
+ RD->bases_begin()->getType()->getAsCXXRecordDecl());
+}
+
+void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T,
+ llvm::Value *Derived,
+ bool MayBeNull) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ auto *ClassTy = T->getAs<RecordType>();
+ if (!ClassTy)
+ return;
+
+ const CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(ClassTy->getDecl());
+
+ if (!ClassDecl->isCompleteDefinition() || !ClassDecl->isDynamicClass())
+ return;
+
+ SmallString<64> MangledName;
+ llvm::raw_svector_ostream Out(MangledName);
+ CGM.getCXXABI().getMangleContext().mangleCXXRTTI(T.getUnqualifiedType(),
+ Out);
+
+ // Blacklist based on the mangled type.
+ if (CGM.getContext().getSanitizerBlacklist().isBlacklistedType(Out.str()))
+ return;
+
+ if (!SanOpts.has(SanitizerKind::CFICastStrict))
+ ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl);
+
+ llvm::BasicBlock *ContBlock = 0;
+
+ if (MayBeNull) {
+ llvm::Value *DerivedNotNull =
+ Builder.CreateIsNotNull(Derived, "cast.nonnull");
+
+ llvm::BasicBlock *CheckBlock = createBasicBlock("cast.check");
+ ContBlock = createBasicBlock("cast.cont");
+
+ Builder.CreateCondBr(DerivedNotNull, CheckBlock, ContBlock);
+
+ EmitBlock(CheckBlock);
+ }
+
+ llvm::Value *VTable = GetVTablePtr(Derived, Int8PtrTy);
+ EmitVTablePtrCheck(ClassDecl, VTable);
+
+ if (MayBeNull) {
+ Builder.CreateBr(ContBlock);
+ EmitBlock(ContBlock);
+ }
+}
+
+void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
+ llvm::Value *VTable) {
// FIXME: Add blacklisting scheme.
if (RD->isInStdNamespace())
return;
OpenPOWER on IntegriCloud