Index: lib/CodeGen/CGExprCXX.cpp =================================================================== --- lib/CodeGen/CGExprCXX.cpp +++ lib/CodeGen/CGExprCXX.cpp @@ -35,17 +35,6 @@ "Trying to emit a member or operator call expr on a static method!"); 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(MD) - ? CodeGenFunction::TCK_ConstructorCall - : CodeGenFunction::TCK_MemberCall, - CallLoc, This, C.getRecordType(MD->getParent())); - // Push the this ptr. const CXXRecordDecl *RD = CGF.CGM.getCXXABI().getThisArgumentTypeForMethod(MD); @@ -293,6 +282,19 @@ 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(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 // 'CalleeDecl' instead. @@ -1923,6 +1925,12 @@ if (E->isArrayForm()) { EmitArrayDelete(*this, E, Ptr, DeleteTy); } else { + SourceLocation CallLoc; + if (E) + CallLoc = E->getExprLoc(); + EmitTypeCheck(CodeGenFunction::TCK_MemberCall, + CallLoc, Ptr.getPointer(), + DeleteTy); EmitObjectDelete(*this, E, Ptr, DeleteTy); } Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -1817,6 +1817,12 @@ assert(CE == nullptr || CE->arg_begin() == CE->arg_end()); assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete); + ASTContext &Context = getContext(); + SourceLocation CallLoc = CE ? CE->getLocStart() : SourceLocation(); + CGF.EmitTypeCheck(CodeGenFunction::TCK_MemberCall, + CallLoc, This.getPointer(), + Context.getRecordType(Dtor->getParent())); + // We have only one destructor in the vftable but can get both behaviors // by passing an implicit int parameter. GlobalDecl GD(Dtor, Dtor_Deleting); @@ -1824,9 +1830,8 @@ Dtor, StructorType::Deleting); llvm::Type *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo); CGCallee Callee = getVirtualFunctionPointer( - CGF, GD, This, Ty, CE ? CE->getLocStart() : SourceLocation()); + CGF, GD, This, Ty, CallLoc); - ASTContext &Context = getContext(); llvm::Value *ImplicitParam = llvm::ConstantInt::get( llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()), DtorType == Dtor_Deleting); Index: test/CodeGenCXX/ubsan-vtable-checks.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/ubsan-vtable-checks.cpp @@ -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 %cont, label %handler.type_mismatch + // 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 %handler.dynamic_type_cache_miss + // 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; +}