Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit bd4a5c6

Browse files
committed
-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
1 parent 857a79e commit bd4a5c6

File tree

4 files changed

+39
-8
lines changed

4 files changed

+39
-8
lines changed

lib/CodeGen/CGExpr.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -570,15 +570,15 @@ static llvm::Value *emitHash16Bytes(CGBuilderTy &Builder, llvm::Value *Low,
570570

571571
bool CodeGenFunction::isNullPointerAllowed(TypeCheckKind TCK) {
572572
return TCK == TCK_DowncastPointer || TCK == TCK_Upcast ||
573-
TCK == TCK_UpcastToVirtualBase;
573+
TCK == TCK_UpcastToVirtualBase || TCK == TCK_DynamicOperation;
574574
}
575575

576576
bool CodeGenFunction::isVptrCheckRequired(TypeCheckKind TCK, QualType Ty) {
577577
CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
578578
return (RD && RD->hasDefinition() && RD->isDynamicClass()) &&
579579
(TCK == TCK_MemberAccess || TCK == TCK_MemberCall ||
580580
TCK == TCK_DowncastPointer || TCK == TCK_DowncastReference ||
581-
TCK == TCK_UpcastToVirtualBase);
581+
TCK == TCK_UpcastToVirtualBase || TCK == TCK_DynamicOperation);
582582
}
583583

584584
bool CodeGenFunction::sanitizePerformTypeCheck() const {

lib/CodeGen/CGExprCXX.cpp

+21-5
Original file line numberDiff line numberDiff line change
@@ -2056,6 +2056,15 @@ static llvm::Value *EmitTypeidFromVTable(CodeGenFunction &CGF, const Expr *E,
20562056
// Get the vtable pointer.
20572057
Address ThisPtr = CGF.EmitLValue(E).getAddress();
20582058

2059+
QualType SrcRecordTy = E->getType();
2060+
2061+
// C++ [class.cdtor]p4:
2062+
// If the operand of typeid refers to the object under construction or
2063+
// destruction and the static type of the operand is neither the constructor
2064+
// or destructor’s class nor one of its bases, the behavior is undefined.
2065+
CGF.EmitTypeCheck(CodeGenFunction::TCK_DynamicOperation, E->getExprLoc(),
2066+
ThisPtr.getPointer(), SrcRecordTy);
2067+
20592068
// C++ [expr.typeid]p2:
20602069
// If the glvalue expression is obtained by applying the unary * operator to
20612070
// 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,
20642073
// However, this paragraph's intent is not clear. We choose a very generous
20652074
// interpretation which implores us to consider comma operators, conditional
20662075
// operators, parentheses and other such constructs.
2067-
QualType SrcRecordTy = E->getType();
20682076
if (CGF.CGM.getCXXABI().shouldTypeidBeNullChecked(
20692077
isGLValueFromPointerDeref(E), SrcRecordTy)) {
20702078
llvm::BasicBlock *BadTypeidBlock =
@@ -2127,10 +2135,6 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(Address ThisAddr,
21272135
CGM.EmitExplicitCastExprType(DCE, this);
21282136
QualType DestTy = DCE->getTypeAsWritten();
21292137

2130-
if (DCE->isAlwaysNull())
2131-
if (llvm::Value *T = EmitDynamicCastToNull(*this, DestTy))
2132-
return T;
2133-
21342138
QualType SrcTy = DCE->getSubExpr()->getType();
21352139

21362140
// C++ [expr.dynamic.cast]p7:
@@ -2151,6 +2155,18 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(Address ThisAddr,
21512155
DestRecordTy = DestTy->castAs<ReferenceType>()->getPointeeType();
21522156
}
21532157

2158+
// C++ [class.cdtor]p5:
2159+
// If the operand of the dynamic_cast refers to the object under
2160+
// construction or destruction and the static type of the operand is not a
2161+
// pointer to or object of the constructor or destructor’s own class or one
2162+
// of its bases, the dynamic_cast results in undefined behavior.
2163+
EmitTypeCheck(TCK_DynamicOperation, DCE->getExprLoc(), ThisAddr.getPointer(),
2164+
SrcRecordTy);
2165+
2166+
if (DCE->isAlwaysNull())
2167+
if (llvm::Value *T = EmitDynamicCastToNull(*this, DestTy))
2168+
return T;
2169+
21542170
assert(SrcRecordTy->isRecordType() && "source type must be a record type!");
21552171

21562172
// C++ [expr.dynamic.cast]p4:

lib/CodeGen/CodeGenFunction.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -2371,7 +2371,10 @@ class CodeGenFunction : public CodeGenTypeCache {
23712371
/// object within its lifetime.
23722372
TCK_UpcastToVirtualBase,
23732373
/// Checking the value assigned to a _Nonnull pointer. Must not be null.
2374-
TCK_NonnullAssign
2374+
TCK_NonnullAssign,
2375+
/// Checking the operand of a dynamic_cast or a typeid expression. Must be
2376+
/// null or an object within its lifetime.
2377+
TCK_DynamicOperation
23752378
};
23762379

23772380
/// Determine whether the pointer type check \p TCK permits null pointers.

test/CodeGenCXX/ubsan-vtable-checks.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,15 @@ void delete_it(T *t) {
3838
// CHECK-VPTR: load {{.*}} (%struct.T*{{.*}})**, {{.*}} (%struct.T*{{.*}})***
3939
delete t;
4040
}
41+
42+
// ITANIUM: define %struct.U* @_Z7dyncastP1T
43+
// MSABI: define %struct.U* @"\01?dyncast
44+
U* dyncast(T *t) {
45+
// First, we check that dynamic_cast is not called before a type check.
46+
// CHECK-VPTR-NOT: call i8* @__{{dynamic_cast|RTDynamicCast}}
47+
// CHECK-VPTR: br i1 {{.*}} label %{{.*}}
48+
// CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort
49+
// Second, we check that dynamic_cast is actually called once the type check is done.
50+
// CHECK-VPTR: call i8* @__{{dynamic_cast|RTDynamicCast}}
51+
return dynamic_cast<U*>(t);
52+
}

0 commit comments

Comments
 (0)