From: Stephan Bergmann Date: Thu, 28 Dec 2017 12:45:41 +0000 (+0000) Subject: -fsanitize=vptr warnings on bad static types in dynamic_cast and typeid X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bd4a5c665b0983aaed3e6c0a1f2e02b89808691e;p=clang -fsanitize=vptr warnings on bad static types in dynamic_cast and typeid ...when such an operation is done on an object during con-/destruction. This is the cfe part of a patch covering both cfe and compiler-rt. Differential Revision: https://reviews.llvm.org/D40295 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@321519 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 90eeddf5cc..c7dc8337e1 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -570,7 +570,7 @@ static llvm::Value *emitHash16Bytes(CGBuilderTy &Builder, llvm::Value *Low, bool CodeGenFunction::isNullPointerAllowed(TypeCheckKind TCK) { return TCK == TCK_DowncastPointer || TCK == TCK_Upcast || - TCK == TCK_UpcastToVirtualBase; + TCK == TCK_UpcastToVirtualBase || TCK == TCK_DynamicOperation; } bool CodeGenFunction::isVptrCheckRequired(TypeCheckKind TCK, QualType Ty) { @@ -578,7 +578,7 @@ bool CodeGenFunction::isVptrCheckRequired(TypeCheckKind TCK, QualType Ty) { return (RD && RD->hasDefinition() && RD->isDynamicClass()) && (TCK == TCK_MemberAccess || TCK == TCK_MemberCall || TCK == TCK_DowncastPointer || TCK == TCK_DowncastReference || - TCK == TCK_UpcastToVirtualBase); + TCK == TCK_UpcastToVirtualBase || TCK == TCK_DynamicOperation); } bool CodeGenFunction::sanitizePerformTypeCheck() const { diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 0749b0ac46..c32f1e5415 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -2056,6 +2056,15 @@ static llvm::Value *EmitTypeidFromVTable(CodeGenFunction &CGF, const Expr *E, // Get the vtable pointer. Address ThisPtr = CGF.EmitLValue(E).getAddress(); + QualType SrcRecordTy = E->getType(); + + // C++ [class.cdtor]p4: + // If the operand of typeid refers to the object under construction or + // destruction and the static type of the operand is neither the constructor + // or destructor’s class nor one of its bases, the behavior is undefined. + CGF.EmitTypeCheck(CodeGenFunction::TCK_DynamicOperation, E->getExprLoc(), + ThisPtr.getPointer(), SrcRecordTy); + // C++ [expr.typeid]p2: // If the glvalue expression is obtained by applying the unary * operator to // a pointer and the pointer is a null pointer value, the typeid expression @@ -2064,7 +2073,6 @@ static llvm::Value *EmitTypeidFromVTable(CodeGenFunction &CGF, const Expr *E, // However, this paragraph's intent is not clear. We choose a very generous // interpretation which implores us to consider comma operators, conditional // operators, parentheses and other such constructs. - QualType SrcRecordTy = E->getType(); if (CGF.CGM.getCXXABI().shouldTypeidBeNullChecked( isGLValueFromPointerDeref(E), SrcRecordTy)) { llvm::BasicBlock *BadTypeidBlock = @@ -2127,10 +2135,6 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(Address ThisAddr, CGM.EmitExplicitCastExprType(DCE, this); QualType DestTy = DCE->getTypeAsWritten(); - if (DCE->isAlwaysNull()) - if (llvm::Value *T = EmitDynamicCastToNull(*this, DestTy)) - return T; - QualType SrcTy = DCE->getSubExpr()->getType(); // C++ [expr.dynamic.cast]p7: @@ -2151,6 +2155,18 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(Address ThisAddr, DestRecordTy = DestTy->castAs()->getPointeeType(); } + // C++ [class.cdtor]p5: + // If the operand of the dynamic_cast refers to the object under + // construction or destruction and the static type of the operand is not a + // pointer to or object of the constructor or destructor’s own class or one + // of its bases, the dynamic_cast results in undefined behavior. + EmitTypeCheck(TCK_DynamicOperation, DCE->getExprLoc(), ThisAddr.getPointer(), + SrcRecordTy); + + if (DCE->isAlwaysNull()) + if (llvm::Value *T = EmitDynamicCastToNull(*this, DestTy)) + return T; + assert(SrcRecordTy->isRecordType() && "source type must be a record type!"); // C++ [expr.dynamic.cast]p4: diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 2f13c4d6e4..dd4c2e43ef 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -2371,7 +2371,10 @@ public: /// object within its lifetime. TCK_UpcastToVirtualBase, /// Checking the value assigned to a _Nonnull pointer. Must not be null. - TCK_NonnullAssign + TCK_NonnullAssign, + /// Checking the operand of a dynamic_cast or a typeid expression. Must be + /// null or an object within its lifetime. + TCK_DynamicOperation }; /// Determine whether the pointer type check \p TCK permits null pointers. diff --git a/test/CodeGenCXX/ubsan-vtable-checks.cpp b/test/CodeGenCXX/ubsan-vtable-checks.cpp index 5e17913a2c..090707c29d 100644 --- a/test/CodeGenCXX/ubsan-vtable-checks.cpp +++ b/test/CodeGenCXX/ubsan-vtable-checks.cpp @@ -38,3 +38,15 @@ void delete_it(T *t) { // CHECK-VPTR: load {{.*}} (%struct.T*{{.*}})**, {{.*}} (%struct.T*{{.*}})*** delete t; } + +// ITANIUM: define %struct.U* @_Z7dyncastP1T +// MSABI: define %struct.U* @"\01?dyncast +U* dyncast(T *t) { + // First, we check that dynamic_cast is not called before a type check. + // CHECK-VPTR-NOT: call i8* @__{{dynamic_cast|RTDynamicCast}} + // CHECK-VPTR: br i1 {{.*}} label %{{.*}} + // CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort + // Second, we check that dynamic_cast is actually called once the type check is done. + // CHECK-VPTR: call i8* @__{{dynamic_cast|RTDynamicCast}} + return dynamic_cast(t); +}