forked from OSchip/llvm-project
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:
parent
48c26b2b12
commit
d98f5d78cb
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue