Insert a type check before reading vtable.

Summary:
this is to prevent a situation when a pointer is invalid or null,
but we get to reading from vtable before we can check that
(possibly causing a segfault without a good diagnostics).

Reviewers: pcc

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D26559

llvm-svn: 287181
This commit is contained in:
Ivan Krasin 2016-11-17 00:39:48 +00:00
parent 48c26b2b12
commit d98f5d78cb
2 changed files with 62 additions and 11 deletions

View File

@ -35,17 +35,6 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD,
"Trying to emit a member or operator call expr on a static method!"); "Trying to emit a member or operator call expr on a static method!");
ASTContext &C = CGF.getContext(); ASTContext &C = CGF.getContext();
// C++11 [class.mfct.non-static]p2:
// If a non-static member function of a class X is called for an object that
// is not of type X, or of a type derived from X, the behavior is undefined.
SourceLocation CallLoc;
if (CE)
CallLoc = CE->getExprLoc();
CGF.EmitTypeCheck(isa<CXXConstructorDecl>(MD)
? CodeGenFunction::TCK_ConstructorCall
: CodeGenFunction::TCK_MemberCall,
CallLoc, This, C.getRecordType(MD->getParent()));
// Push the this ptr. // Push the this ptr.
const CXXRecordDecl *RD = const CXXRecordDecl *RD =
CGF.CGM.getCXXABI().getThisArgumentTypeForMethod(MD); CGF.CGM.getCXXABI().getThisArgumentTypeForMethod(MD);
@ -293,6 +282,19 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
llvm::FunctionType *Ty = CGM.getTypes().GetFunctionType(*FInfo); llvm::FunctionType *Ty = CGM.getTypes().GetFunctionType(*FInfo);
// C++11 [class.mfct.non-static]p2:
// If a non-static member function of a class X is called for an object that
// is not of type X, or of a type derived from X, the behavior is undefined.
SourceLocation CallLoc;
ASTContext &C = getContext();
if (CE)
CallLoc = CE->getExprLoc();
EmitTypeCheck(isa<CXXConstructorDecl>(CalleeDecl)
? CodeGenFunction::TCK_ConstructorCall
: CodeGenFunction::TCK_MemberCall,
CallLoc, This.getPointer(), C.getRecordType(CalleeDecl->getParent()));
// FIXME: Uses of 'MD' past this point need to be audited. We may need to use // FIXME: Uses of 'MD' past this point need to be audited. We may need to use
// 'CalleeDecl' instead. // 'CalleeDecl' instead.
@ -1763,6 +1765,15 @@ static void EmitObjectDelete(CodeGenFunction &CGF,
const CXXDeleteExpr *DE, const CXXDeleteExpr *DE,
Address Ptr, Address Ptr,
QualType ElementType) { QualType ElementType) {
// C++11 [expr.delete]p3:
// If the static type of the object to be deleted is different from its
// dynamic type, the static type shall be a base class of the dynamic type
// of the object to be deleted and the static type shall have a virtual
// destructor or the behavior is undefined.
CGF.EmitTypeCheck(CodeGenFunction::TCK_MemberCall,
DE->getExprLoc(), Ptr.getPointer(),
ElementType);
// Find the destructor for the type, if applicable. If the // Find the destructor for the type, if applicable. If the
// destructor is virtual, we'll just emit the vcall and return. // destructor is virtual, we'll just emit the vcall and return.
const CXXDestructorDecl *Dtor = nullptr; const CXXDestructorDecl *Dtor = nullptr;

View File

@ -0,0 +1,40 @@
// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -emit-llvm -fsanitize=null %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NULL --check-prefix=ITANIUM
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=null %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NULL --check-prefix=MSABI
// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -emit-llvm -fsanitize=vptr %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-VPTR --check-prefix=ITANIUM
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=vptr %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-VPTR --check-prefix=MSABI
struct T {
virtual ~T() {}
virtual int v() { return 1; }
};
struct U : T {
~U();
virtual int v() { return 2; }
};
U::~U() {}
// ITANIUM: define i32 @_Z5get_vP1T
// MSABI: define i32 @"\01?get_v
int get_v(T* t) {
// First, we check that vtable is not loaded before a type check.
// CHECK-NULL-NOT: load {{.*}} (%struct.T*{{.*}})**, {{.*}} (%struct.T*{{.*}})***
// CHECK-NULL: [[UBSAN_CMP_RES:%[0-9]+]] = icmp ne %struct.T* %{{[_a-z0-9]+}}, null
// CHECK-NULL-NEXT: br i1 [[UBSAN_CMP_RES]], label %{{.*}}, label %{{.*}}
// CHECK-NULL: call void @__ubsan_handle_type_mismatch_abort
// Second, we check that vtable is actually loaded once the type check is done.
// CHECK-NULL: load {{.*}} (%struct.T*{{.*}})**, {{.*}} (%struct.T*{{.*}})***
return t->v();
}
// ITANIUM: define void @_Z9delete_itP1T
// MSABI: define void @"\01?delete_it
void delete_it(T *t) {
// First, we check that vtable is not loaded before a type check.
// CHECK-VPTR-NOT: load {{.*}} (%struct.T*{{.*}})**, {{.*}} (%struct.T*{{.*}})***
// CHECK-VPTR: br i1 {{.*}} label %{{.*}}
// CHECK-VPTR: call void @__ubsan_handle_dynamic_type_cache_miss_abort
// Second, we check that vtable is actually loaded once the type check is done.
// CHECK-VPTR: load {{.*}} (%struct.T*{{.*}})**, {{.*}} (%struct.T*{{.*}})***
delete t;
}