Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -619,8 +619,11 @@ // -- the [pointer or glvalue] is used to access a non-static data member // or call a non-static member function CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); + bool SupportedMemberCall = (TCK == TCK_MemberCall) + ? !SanitizerBasePointerBlacklist.count(Ptr) + : false; if (SanOpts.has(SanitizerKind::Vptr) && - (TCK == TCK_MemberAccess || TCK == TCK_MemberCall || + (TCK == TCK_MemberAccess || SupportedMemberCall || 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 @@ -194,6 +194,8 @@ else This = EmitLValue(Base).getAddress(); + if (SanOpts.has(SanitizerKind::Vptr) && DevirtualizedMethod) + SanitizerBasePointerBlacklist.insert(This.getPointer()); if (MD->isTrivial() || (MD->isDefaulted() && MD->getParent()->isUnion())) { if (isa(MD)) return RValue::get(nullptr); Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -33,6 +33,7 @@ #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/ValueHandle.h" #include "llvm/Support/Debug.h" @@ -374,6 +375,9 @@ return DominatingValue::save(*this, value); } + /// Set of object pointers which are blacklisted from the UB sanitizer. + llvm::SmallPtrSet SanitizerBasePointerBlacklist; + public: /// ObjCEHValueStack - Stack of Objective-C exception values, used for /// rethrows. 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 +}