Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -531,7 +531,8 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, llvm::Value *Ptr, QualType Ty, - CharUnits Alignment, bool SkipNullCheck) { + CharUnits Alignment, bool SkipNullCheck, + bool SkipMemberCallCheck) { if (!sanitizePerformTypeCheck()) return; @@ -620,7 +621,8 @@ // or call a non-static member function CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); if (SanOpts.has(SanitizerKind::Vptr) && - (TCK == TCK_MemberAccess || TCK == TCK_MemberCall || + (TCK == TCK_MemberAccess || + (TCK == TCK_MemberCall && !SkipMemberCallCheck) || TCK == TCK_DowncastPointer || TCK == TCK_DowncastReference || TCK == TCK_UpcastToVirtualBase) && RD && RD->hasDefinition() && RD->isDynamicClass()) { Index: lib/CodeGen/CGExprCXX.cpp =================================================================== --- lib/CodeGen/CGExprCXX.cpp +++ lib/CodeGen/CGExprCXX.cpp @@ -28,7 +28,8 @@ commonEmitCXXMemberOrOperatorCall(CodeGenFunction &CGF, const CXXMethodDecl *MD, llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy, const CallExpr *CE, - CallArgList &Args, CallArgList *RtlArgs) { + CallArgList &Args, CallArgList *RtlArgs, + bool SkipMemberCallCheck) { assert(CE == nullptr || isa(CE) || isa(CE)); assert(MD->isInstance() && @@ -44,7 +45,9 @@ CGF.EmitTypeCheck(isa(MD) ? CodeGenFunction::TCK_ConstructorCall : CodeGenFunction::TCK_MemberCall, - CallLoc, This, C.getRecordType(MD->getParent())); + CallLoc, This, C.getRecordType(MD->getParent()), + /*Alignment=*/CharUnits::Zero(), /*SkipNullCheck=*/false, + SkipMemberCallCheck); // Push the this ptr. const CXXRecordDecl *RD = @@ -82,11 +85,12 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall( const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue, llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy, - const CallExpr *CE, CallArgList *RtlArgs) { + const CallExpr *CE, CallArgList *RtlArgs, bool SkipMemberCallCheck) { const FunctionProtoType *FPT = MD->getType()->castAs(); CallArgList Args; RequiredArgs required = commonEmitCXXMemberOrOperatorCall( - *this, MD, This, ImplicitParam, ImplicitParamTy, CE, Args, RtlArgs); + *this, MD, This, ImplicitParam, ImplicitParamTy, CE, Args, RtlArgs, + SkipMemberCallCheck); return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required), Callee, ReturnValue, Args, MD); } @@ -97,7 +101,7 @@ StructorType Type) { CallArgList Args; commonEmitCXXMemberOrOperatorCall(*this, DD, This, ImplicitParam, - ImplicitParamTy, CE, Args, nullptr); + ImplicitParamTy, CE, Args, nullptr, false); return EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(DD, Type), Callee, ReturnValueSlot(), Args, DD); } @@ -175,6 +179,10 @@ } } + // If we can devirtualize the method, don't emit instrumentation to check + // the dynamic type of the object pointer. + bool SkipMemberCallCheck = DevirtualizedMethod != nullptr; + // C++17 demands that we evaluate the RHS of a (possibly-compound) assignment // operator before the LHS. CallArgList RtlArgStorage; @@ -270,7 +278,7 @@ } EmitCXXMemberOrOperatorCall(MD, Callee, ReturnValue, This.getPointer(), /*ImplicitParam=*/nullptr, QualType(), CE, - nullptr); + nullptr, SkipMemberCallCheck); } return RValue::get(nullptr); } @@ -304,7 +312,7 @@ return EmitCXXMemberOrOperatorCall(MD, Callee, ReturnValue, This.getPointer(), /*ImplicitParam=*/nullptr, QualType(), CE, - RtlArgs); + RtlArgs, SkipMemberCallCheck); } RValue Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -2084,7 +2084,8 @@ /// appropriate size and alignment for an object of type \p Type. void EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, llvm::Value *V, QualType Type, CharUnits Alignment = CharUnits::Zero(), - bool SkipNullCheck = false); + bool SkipNullCheck = false, + bool SkipMemberCallCheck = false); /// \brief Emit a check that \p Base points into an array object, which /// we can access at index \p Index. \p Accessed should be \c false if we @@ -2882,7 +2883,7 @@ ReturnValueSlot ReturnValue, llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy, const CallExpr *E, - CallArgList *RtlArgs); + CallArgList *RtlArgs, bool SkipMemberCallCheck); RValue EmitCXXDestructorCall(const CXXDestructorDecl *DD, llvm::Value *Callee, llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy, const CallExpr *E, Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -1456,8 +1456,8 @@ Callee = CGM.getAddrOfCXXStructor(DD, getFromDtorType(Type)); CGF.EmitCXXMemberOrOperatorCall(DD, Callee, ReturnValueSlot(), - This.getPointer(), VTT, VTTTy, - nullptr, nullptr); + This.getPointer(), VTT, VTTTy, nullptr, + nullptr, /*SkipMemberCallCheck=*/false); } void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, @@ -1637,7 +1637,8 @@ CGF.EmitCXXMemberOrOperatorCall(Dtor, Callee, ReturnValueSlot(), This.getPointer(), /*ImplicitParam=*/nullptr, - QualType(), CE, nullptr); + QualType(), CE, nullptr, + /*SkipMemberCallCheck=*/false); return nullptr; } Index: test/CodeGenCXX/ubsan-devirtualized-calls.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/ubsan-devirtualized-calls.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -emit-llvm -fsanitize=vptr %s -o - | FileCheck %s + +struct Base1 { + virtual int f1() { return 1; } +}; + +struct Base2 { + virtual int f1() { return 2; } +}; + +struct Derived1 final : Base1 { + int f1() override { return 3; } +}; + +struct Derived2 final : Base1, Base2 { + int f1() override { return 3; } +}; + +// CHECK-LABEL: define i32 @_Z2t1v +int t1() { + Derived1 d1; + return static_cast(&d1)->f1(); +// CHECK-NOT: !nosanitize +} + +// CHECK-LABEL: define i32 @_Z2t2v +int t2() { + Derived2 d2; + return static_cast(&d2)->f1(); +// CHECK-NOT: !nosanitize +} + +// CHECK-LABEL: define i32 @_Z2t3v +int t3() { + Derived2 d2; + return static_cast(&d2)->f1(); +// CHECK-NOT: !nosanitize +}