فهرست منبع

[cfi] Safe handling of unaddressable vtable pointers (clang).

Avoid crashing when printing diagnostics for vtable-related CFI
errors. In diagnostic mode, the frontend does an additional check of
the vtable pointer against the set of all known vtable addresses and
lets the runtime handler know if it is safe to inspect the vtable.

http://reviews.llvm.org/D16823

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259716 91177308-0d34-0410-b5e6-96231b3b80d8
Evgeniy Stepanov 9 سال پیش
والد
کامیت
388a054401

+ 15 - 3
lib/CodeGen/CGClass.cpp

@@ -2607,10 +2607,22 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
   auto TypeId = CGM.CreateCfiIdForTypeMetadata(MD);
   auto TypeId = CGM.CreateCfiIdForTypeMetadata(MD);
   if (CGM.getCodeGenOpts().SanitizeCfiCrossDso && TypeId) {
   if (CGM.getCodeGenOpts().SanitizeCfiCrossDso && TypeId) {
     EmitCfiSlowPathCheck(M, BitSetTest, TypeId, CastedVTable, StaticData);
     EmitCfiSlowPathCheck(M, BitSetTest, TypeId, CastedVTable, StaticData);
-  } else {
-    EmitCheck(std::make_pair(BitSetTest, M), "cfi_check_fail", StaticData,
-              CastedVTable);
+    return;
   }
   }
+
+  if (CGM.getCodeGenOpts().SanitizeTrap.has(M)) {
+    EmitTrapCheck(BitSetTest);
+    return;
+  }
+
+  llvm::Value *AllVtables = llvm::MetadataAsValue::get(
+      CGM.getLLVMContext(),
+      llvm::MDString::get(CGM.getLLVMContext(), "all-vtables"));
+  llvm::Value *ValidVtable =
+      Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test),
+                         {CastedVTable, AllVtables});
+  EmitCheck(std::make_pair(BitSetTest, M), "cfi_check_fail", StaticData,
+            {CastedVTable, ValidVtable});
 }
 }
 
 
 // FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do
 // FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do

+ 12 - 2
lib/CodeGen/CGExpr.cpp

@@ -2636,6 +2636,14 @@ void CodeGenFunction::EmitCfiCheckFail() {
   Address CheckKindAddr(V, getIntAlign());
   Address CheckKindAddr(V, getIntAlign());
   llvm::Value *CheckKind = Builder.CreateLoad(CheckKindAddr);
   llvm::Value *CheckKind = Builder.CreateLoad(CheckKindAddr);
 
 
+  llvm::Value *AllVtables = llvm::MetadataAsValue::get(
+      CGM.getLLVMContext(),
+      llvm::MDString::get(CGM.getLLVMContext(), "all-vtables"));
+  llvm::Value *ValidVtable = Builder.CreateZExt(
+      Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test),
+                         {Addr, AllVtables}),
+      IntPtrTy);
+
   const std::pair<int, SanitizerMask> CheckKinds[] = {
   const std::pair<int, SanitizerMask> CheckKinds[] = {
       {CFITCK_VCall, SanitizerKind::CFIVCall},
       {CFITCK_VCall, SanitizerKind::CFIVCall},
       {CFITCK_NVCall, SanitizerKind::CFINVCall},
       {CFITCK_NVCall, SanitizerKind::CFINVCall},
@@ -2649,7 +2657,8 @@ void CodeGenFunction::EmitCfiCheckFail() {
     SanitizerMask Mask = CheckKindMaskPair.second;
     SanitizerMask Mask = CheckKindMaskPair.second;
     llvm::Value *Cond =
     llvm::Value *Cond =
         Builder.CreateICmpNE(CheckKind, llvm::ConstantInt::get(Int8Ty, Kind));
         Builder.CreateICmpNE(CheckKind, llvm::ConstantInt::get(Int8Ty, Kind));
-    EmitCheck(std::make_pair(Cond, Mask), "cfi_check_fail", {}, {Data, Addr});
+    EmitCheck(std::make_pair(Cond, Mask), "cfi_check_fail", {},
+              {Data, Addr, ValidVtable});
   }
   }
 
 
   FinishFunction();
   FinishFunction();
@@ -3970,7 +3979,8 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
                            CastedCallee, StaticData);
                            CastedCallee, StaticData);
     } else {
     } else {
       EmitCheck(std::make_pair(BitSetTest, SanitizerKind::CFIICall),
       EmitCheck(std::make_pair(BitSetTest, SanitizerKind::CFIICall),
-                "cfi_check_fail", StaticData, CastedCallee);
+                "cfi_check_fail", StaticData,
+                {CastedCallee, llvm::UndefValue::get(IntPtrTy)});
     }
     }
   }
   }
 
 

+ 25 - 0
lib/CodeGen/CodeGenModule.cpp

@@ -4021,6 +4021,20 @@ llvm::Metadata *CodeGenModule::CreateMetadataIdentifierForType(QualType T) {
   return InternalId;
   return InternalId;
 }
 }
 
 
+/// Returns whether this module needs the "all-vtables" bitset.
+bool CodeGenModule::NeedAllVtablesBitSet() const {
+  // Returns true if at least one of vtable-based CFI checkers is enabled and
+  // is not in the trapping mode.
+  return ((LangOpts.Sanitize.has(SanitizerKind::CFIVCall) &&
+           !CodeGenOpts.SanitizeTrap.has(SanitizerKind::CFIVCall)) ||
+          (LangOpts.Sanitize.has(SanitizerKind::CFINVCall) &&
+           !CodeGenOpts.SanitizeTrap.has(SanitizerKind::CFINVCall)) ||
+          (LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) &&
+           !CodeGenOpts.SanitizeTrap.has(SanitizerKind::CFIDerivedCast)) ||
+          (LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast) &&
+           !CodeGenOpts.SanitizeTrap.has(SanitizerKind::CFIUnrelatedCast)));
+}
+
 void CodeGenModule::CreateVTableBitSetEntry(llvm::NamedMDNode *BitsetsMD,
 void CodeGenModule::CreateVTableBitSetEntry(llvm::NamedMDNode *BitsetsMD,
                                             llvm::GlobalVariable *VTable,
                                             llvm::GlobalVariable *VTable,
                                             CharUnits Offset,
                                             CharUnits Offset,
@@ -4043,6 +4057,17 @@ void CodeGenModule::CreateVTableBitSetEntry(llvm::NamedMDNode *BitsetsMD,
       BitsetsMD->addOperand(llvm::MDTuple::get(getLLVMContext(), BitsetOps2));
       BitsetsMD->addOperand(llvm::MDTuple::get(getLLVMContext(), BitsetOps2));
     }
     }
   }
   }
+
+  if (NeedAllVtablesBitSet()) {
+    llvm::Metadata *MD = llvm::MDString::get(getLLVMContext(), "all-vtables");
+    llvm::Metadata *BitsetOps[] = {
+        MD, llvm::ConstantAsMetadata::get(VTable),
+        llvm::ConstantAsMetadata::get(
+            llvm::ConstantInt::get(Int64Ty, Offset.getQuantity()))};
+    // Avoid adding a node to BitsetsMD twice.
+    if (!llvm::MDTuple::getIfExists(getLLVMContext(), BitsetOps))
+      BitsetsMD->addOperand(llvm::MDTuple::get(getLLVMContext(), BitsetOps));
+  }
 }
 }
 
 
 // Fills in the supplied string map with the set of target features for the
 // Fills in the supplied string map with the set of target features for the

+ 3 - 0
lib/CodeGen/CodeGenModule.h

@@ -1128,6 +1128,9 @@ public:
   /// Create a bitset entry for the given function and add it to BitsetsMD.
   /// Create a bitset entry for the given function and add it to BitsetsMD.
   void CreateFunctionBitSetEntry(const FunctionDecl *FD, llvm::Function *F);
   void CreateFunctionBitSetEntry(const FunctionDecl *FD, llvm::Function *F);
 
 
+  /// Returns whether this module needs the "all-vtables" bitset.
+  bool NeedAllVtablesBitSet() const;
+
   /// Create a bitset entry for the given vtable and add it to BitsetsMD.
   /// Create a bitset entry for the given vtable and add it to BitsetsMD.
   void CreateVTableBitSetEntry(llvm::NamedMDNode *BitsetsMD,
   void CreateVTableBitSetEntry(llvm::NamedMDNode *BitsetsMD,
                                llvm::GlobalVariable *VTable, CharUnits Offset,
                                llvm::GlobalVariable *VTable, CharUnits Offset,

+ 5 - 3
test/CodeGen/cfi-check-fail.c

@@ -22,13 +22,15 @@ void caller(void (*f)()) {
 // CHECK:   %[[A:.*]] = bitcast i8* %[[DATA]] to { i8, { i8*, i32, i32 }, i8* }*
 // CHECK:   %[[A:.*]] = bitcast i8* %[[DATA]] to { i8, { i8*, i32, i32 }, i8* }*
 // CHECK:   %[[KINDPTR:.*]] = getelementptr {{.*}} %[[A]], i32 0, i32 0
 // CHECK:   %[[KINDPTR:.*]] = getelementptr {{.*}} %[[A]], i32 0, i32 0
 // CHECK:   %[[KIND:.*]] = load i8, i8* %[[KINDPTR]], align 4
 // CHECK:   %[[KIND:.*]] = load i8, i8* %[[KINDPTR]], align 4
+// CHECK:   %[[VTVALID0:.*]] = call i1 @llvm.bitset.test(i8* %[[ADDR]], metadata !"all-vtables")
+// CHECK:   %[[VTVALID:.*]] = zext i1 %[[VTVALID0]] to i64
 // CHECK:   %[[NOT_0:.*]] = icmp ne i8 %[[KIND]], 0
 // CHECK:   %[[NOT_0:.*]] = icmp ne i8 %[[KIND]], 0
 // CHECK:   br i1 %[[NOT_0]], label %[[CONT1:.*]], label %[[HANDLE0:.*]], !prof
 // CHECK:   br i1 %[[NOT_0]], label %[[CONT1:.*]], label %[[HANDLE0:.*]], !prof
 
 
 // CHECK: [[HANDLE0]]:
 // CHECK: [[HANDLE0]]:
 // CHECK:   %[[DATA0:.*]] = ptrtoint i8* %[[DATA]] to i64,
 // CHECK:   %[[DATA0:.*]] = ptrtoint i8* %[[DATA]] to i64,
 // CHECK:   %[[ADDR0:.*]] = ptrtoint i8* %[[ADDR]] to i64,
 // CHECK:   %[[ADDR0:.*]] = ptrtoint i8* %[[ADDR]] to i64,
-// CHECK:   call void @__ubsan_handle_cfi_check_fail(i64 %[[DATA0]], i64 %[[ADDR0]])
+// CHECK:   call void @__ubsan_handle_cfi_check_fail(i64 %[[DATA0]], i64 %[[ADDR0]], i64 %[[VTVALID]])
 // CHECK:   br label %[[CONT1]]
 // CHECK:   br label %[[CONT1]]
 
 
 // CHECK: [[CONT1]]:
 // CHECK: [[CONT1]]:
@@ -46,7 +48,7 @@ void caller(void (*f)()) {
 // CHECK: [[HANDLE2]]:
 // CHECK: [[HANDLE2]]:
 // CHECK:   %[[DATA2:.*]] = ptrtoint i8* %[[DATA]] to i64,
 // CHECK:   %[[DATA2:.*]] = ptrtoint i8* %[[DATA]] to i64,
 // CHECK:   %[[ADDR2:.*]] = ptrtoint i8* %[[ADDR]] to i64,
 // CHECK:   %[[ADDR2:.*]] = ptrtoint i8* %[[ADDR]] to i64,
-// CHECK:   call void @__ubsan_handle_cfi_check_fail_abort(i64 %[[DATA2]], i64 %[[ADDR2]])
+// CHECK:   call void @__ubsan_handle_cfi_check_fail_abort(i64 %[[DATA2]], i64 %[[ADDR2]], i64 %[[VTVALID]])
 // CHECK:   unreachable
 // CHECK:   unreachable
 
 
 // CHECK: [[CONT3]]:
 // CHECK: [[CONT3]]:
@@ -56,7 +58,7 @@ void caller(void (*f)()) {
 // CHECK: [[HANDLE3]]:
 // CHECK: [[HANDLE3]]:
 // CHECK:   %[[DATA3:.*]] = ptrtoint i8* %[[DATA]] to i64,
 // CHECK:   %[[DATA3:.*]] = ptrtoint i8* %[[DATA]] to i64,
 // CHECK:   %[[ADDR3:.*]] = ptrtoint i8* %[[ADDR]] to i64,
 // CHECK:   %[[ADDR3:.*]] = ptrtoint i8* %[[ADDR]] to i64,
-// CHECK:   call void @__ubsan_handle_cfi_check_fail(i64 %[[DATA3]], i64 %[[ADDR3]])
+// CHECK:   call void @__ubsan_handle_cfi_check_fail(i64 %[[DATA3]], i64 %[[ADDR3]], i64 %[[VTVALID]])
 // CHECK:   br label %[[CONT4]]
 // CHECK:   br label %[[CONT4]]
 
 
 // CHECK: [[CONT4]]:
 // CHECK: [[CONT4]]:

+ 11 - 11
test/CodeGenCXX/cfi-cast.cpp

@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-derived-cast -fsanitize-trap=cfi-derived-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-DCAST %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-unrelated-cast -fsanitize-trap=cfi-unrelated-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-unrelated-cast,cfi-cast-strict -fsanitize-trap=cfi-unrelated-cast,cfi-cast-strict -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST-STRICT %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c++11 -fsanitize=cfi-derived-cast -fsanitize-trap=cfi-derived-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-DCAST %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c++11 -fsanitize=cfi-unrelated-cast -fsanitize-trap=cfi-unrelated-cast -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -std=c++11 -fsanitize=cfi-unrelated-cast,cfi-cast-strict -fsanitize-trap=cfi-unrelated-cast,cfi-cast-strict -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST-STRICT %s
 
 
 // In this test the main thing we are searching for is something like
 // In this test the main thing we are searching for is something like
 // 'metadata !"1B"' where "1B" is the mangled name of the class we are
 // 'metadata !"1B"' where "1B" is the mangled name of the class we are
@@ -28,7 +28,7 @@ void abp(A *a) {
 
 
   // CHECK-DCAST: [[CONTBB]]
   // CHECK-DCAST: [[CONTBB]]
   // CHECK-DCAST: ret
   // CHECK-DCAST: ret
-  static_cast<B*>(a);
+  (void)static_cast<B*>(a);
 }
 }
 
 
 // CHECK-DCAST-LABEL: define void @_Z3abrR1A
 // CHECK-DCAST-LABEL: define void @_Z3abrR1A
@@ -42,7 +42,7 @@ void abr(A &a) {
 
 
   // CHECK-DCAST: [[CONTBB]]
   // CHECK-DCAST: [[CONTBB]]
   // CHECK-DCAST: ret
   // CHECK-DCAST: ret
-  static_cast<B&>(a);
+  (void)static_cast<B&>(a);
 }
 }
 
 
 // CHECK-DCAST-LABEL: define void @_Z4abrrO1A
 // CHECK-DCAST-LABEL: define void @_Z4abrrO1A
@@ -56,7 +56,7 @@ void abrr(A &&a) {
 
 
   // CHECK-DCAST: [[CONTBB]]
   // CHECK-DCAST: [[CONTBB]]
   // CHECK-DCAST: ret
   // CHECK-DCAST: ret
-  static_cast<B&&>(a);
+  (void)static_cast<B&&>(a);
 }
 }
 
 
 // CHECK-UCAST-LABEL: define void @_Z3vbpPv
 // CHECK-UCAST-LABEL: define void @_Z3vbpPv
@@ -70,7 +70,7 @@ void vbp(void *p) {
 
 
   // CHECK-UCAST: [[CONTBB]]
   // CHECK-UCAST: [[CONTBB]]
   // CHECK-UCAST: ret
   // CHECK-UCAST: ret
-  static_cast<B*>(p);
+  (void)static_cast<B*>(p);
 }
 }
 
 
 // CHECK-UCAST-LABEL: define void @_Z3vbrRc
 // CHECK-UCAST-LABEL: define void @_Z3vbrRc
@@ -84,7 +84,7 @@ void vbr(char &r) {
 
 
   // CHECK-UCAST: [[CONTBB]]
   // CHECK-UCAST: [[CONTBB]]
   // CHECK-UCAST: ret
   // CHECK-UCAST: ret
-  reinterpret_cast<B&>(r);
+  (void)reinterpret_cast<B&>(r);
 }
 }
 
 
 // CHECK-UCAST-LABEL: define void @_Z4vbrrOc
 // CHECK-UCAST-LABEL: define void @_Z4vbrrOc
@@ -98,7 +98,7 @@ void vbrr(char &&r) {
 
 
   // CHECK-UCAST: [[CONTBB]]
   // CHECK-UCAST: [[CONTBB]]
   // CHECK-UCAST: ret
   // CHECK-UCAST: ret
-  reinterpret_cast<B&&>(r);
+  (void)reinterpret_cast<B&&>(r);
 }
 }
 
 
 // CHECK-UCAST-LABEL: define void @_Z3vcpPv
 // CHECK-UCAST-LABEL: define void @_Z3vcpPv
@@ -106,7 +106,7 @@ void vbrr(char &&r) {
 void vcp(void *p) {
 void vcp(void *p) {
   // CHECK-UCAST: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A")
   // CHECK-UCAST: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A")
   // CHECK-UCAST-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C")
   // CHECK-UCAST-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C")
-  static_cast<C*>(p);
+  (void)static_cast<C*>(p);
 }
 }
 
 
 // CHECK-UCAST-LABEL: define void @_Z3bcpP1B
 // CHECK-UCAST-LABEL: define void @_Z3bcpP1B
@@ -114,7 +114,7 @@ void vcp(void *p) {
 void bcp(B *p) {
 void bcp(B *p) {
   // CHECK-UCAST: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A")
   // CHECK-UCAST: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A")
   // CHECK-UCAST-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C")
   // CHECK-UCAST-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C")
-  (C *)p;
+  (void)(C *)p;
 }
 }
 
 
 // CHECK-UCAST-LABEL: define void @_Z8bcp_callP1B
 // CHECK-UCAST-LABEL: define void @_Z8bcp_callP1B

+ 25 - 8
test/CodeGenCXX/cfi-vcall.cpp

@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=NDIAG %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-ABORT %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=ITANIUM-NDIAG --check-prefix=NDIAG %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=ITANIUM-DIAG --check-prefix=DIAG --check-prefix=DIAG-ABORT %s
 // RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s
 // RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -fsanitize-recover=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM --check-prefix=DIAG --check-prefix=DIAG-RECOVER %s
 // RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=MS --check-prefix=NDIAG %s
 // RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=MS --check-prefix=NDIAG %s
 
 
@@ -55,13 +55,14 @@ void D::h() {
 
 
 // DIAG: @[[SRC:.*]] = private unnamed_addr constant [{{.*}} x i8] c"{{.*}}cfi-vcall.cpp\00", align 1
 // DIAG: @[[SRC:.*]] = private unnamed_addr constant [{{.*}} x i8] c"{{.*}}cfi-vcall.cpp\00", align 1
 // DIAG: @[[TYPE:.*]] = private unnamed_addr constant { i16, i16, [4 x i8] } { i16 -1, i16 0, [4 x i8] c"'A'\00" }
 // DIAG: @[[TYPE:.*]] = private unnamed_addr constant { i16, i16, [4 x i8] } { i16 -1, i16 0, [4 x i8] c"'A'\00" }
-// DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { i8, { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }* } { i8 0, { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 [[@LINE+21]], i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]] }
+// DIAG: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { i8, { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }* } { i8 0, { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 [[@LINE+23]], i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]] }
 
 
 // ITANIUM: define void @_Z2afP1A
 // ITANIUM: define void @_Z2afP1A
 // MS: define void @"\01?af@@YAXPEAUA@@@Z"
 // MS: define void @"\01?af@@YAXPEAUA@@@Z"
 void af(A *a) {
 void af(A *a) {
   // ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A")
   // ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A")
   // MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@")
   // MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@")
+  // DIAG-NEXT: [[VTVALID0:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT]], metadata !"all-vtables")
   // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]]
   // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]]
   // CHECK-NEXT: {{^$}}
   // CHECK-NEXT: {{^$}}
 
 
@@ -69,9 +70,10 @@ void af(A *a) {
   // NDIAG-NEXT: call void @llvm.trap()
   // NDIAG-NEXT: call void @llvm.trap()
   // NDIAG-NEXT: unreachable
   // NDIAG-NEXT: unreachable
   // DIAG-NEXT: [[VTINT:%[^ ]*]] = ptrtoint i8* [[VT]] to i64
   // DIAG-NEXT: [[VTINT:%[^ ]*]] = ptrtoint i8* [[VT]] to i64
-  // DIAG-ABORT-NEXT: call void @__ubsan_handle_cfi_check_fail_abort(i8* getelementptr inbounds ({{.*}} @[[BADTYPESTATIC]], i32 0, i32 0), i64 [[VTINT]])
+  // DIAG-NEXT: [[VTVALID:%[^ ]*]] = zext i1 [[VTVALID0]] to i64
+  // DIAG-ABORT-NEXT: call void @__ubsan_handle_cfi_check_fail_abort(i8* getelementptr inbounds ({{.*}} @[[BADTYPESTATIC]], i32 0, i32 0), i64 [[VTINT]], i64 [[VTVALID]])
   // DIAG-ABORT-NEXT: unreachable
   // DIAG-ABORT-NEXT: unreachable
-  // DIAG-RECOVER-NEXT: call void @__ubsan_handle_cfi_check_fail(i8* getelementptr inbounds ({{.*}} @[[BADTYPESTATIC]], i32 0, i32 0), i64 [[VTINT]])
+  // DIAG-RECOVER-NEXT: call void @__ubsan_handle_cfi_check_fail(i8* getelementptr inbounds ({{.*}} @[[BADTYPESTATIC]], i32 0, i32 0), i64 [[VTINT]], i64 [[VTVALID]])
   // DIAG-RECOVER-NEXT: br label %[[CONTBB]]
   // DIAG-RECOVER-NEXT: br label %[[CONTBB]]
 
 
   // CHECK: [[CONTBB]]
   // CHECK: [[CONTBB]]
@@ -157,20 +159,28 @@ void f(D *d) {
 
 
 }
 }
 
 
-// Check for the expected number of elements (9 or 15 respectively).
-// MS: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){8}]]}
-// ITANIUM: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){14}]]}
+// Check for the expected number of elements (15 or 23 respectively).
+// MS-NDIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){9}]]}
+// MS-DIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){15}]]}
+// ITANIUM-NDIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){14}]]}
+// ITANIUM-DIAG: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){23}]]}
 
 
 // ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16}
 // ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16}
+// ITANIUM-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @_ZTV1A, i64 16}
 // ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
+// ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64}
 // ITANIUM-DAG: !{!"_ZTS1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64}
+// ITANIUM-DIAG-DAG: !{!"all-vtables", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64}
 // ITANIUM-DAG: !{!"_ZTS1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
+// ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88}
 // ITANIUM-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88}
+// ITANIUM-DIAG-DAG: !{!"all-vtables", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88}
 // ITANIUM-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // ITANIUM-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTV1B, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1A", [7 x i8*]* @_ZTV1B, i64 32}
+// ITANIUM-DIAG-DAG: !{!"all-vtables", [7 x i8*]* @_ZTV1B, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTV1B, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1B", [7 x i8*]* @_ZTV1B, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1A", [5 x i8*]* @_ZTV1C, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1A", [5 x i8*]* @_ZTV1C, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32}
 // ITANIUM-DAG: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32}
@@ -178,11 +188,18 @@ void f(D *d) {
 // ITANIUM-DAG: !{!{{[0-9]+}}, [3 x i8*]* @_ZTVZ3foovE2FA, i64 16}
 // ITANIUM-DAG: !{!{{[0-9]+}}, [3 x i8*]* @_ZTVZ3foovE2FA, i64 16}
 
 
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTA]], i64 8}
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTA]], i64 8}
+// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTA]], i64 8}
 // MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8}
 // MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8}
+// MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTB]], i64 8}
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8}
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8}
+// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinB]], i64 8}
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8}
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8}
+// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinC]], i64 8}
 // MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTBinD]], i64 8}
 // MS-DAG: !{!"?AUB@@", [3 x i8*]* @[[VTBinD]], i64 8}
+// MS-DIAG-DAG: !{!"all-vtables", [3 x i8*]* @[[VTBinD]], i64 8}
 // MS-DAG: !{![[DTYPE]], [3 x i8*]* @[[VTBinD]], i64 8}
 // MS-DAG: !{![[DTYPE]], [3 x i8*]* @[[VTBinD]], i64 8}
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinBinD]], i64 8}
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinBinD]], i64 8}
+// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTAinBinD]], i64 8}
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTFA]], i64 8}
 // MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTFA]], i64 8}
+// MS-DIAG-DAG: !{!"all-vtables", [2 x i8*]* @[[VTFA]], i64 8}
 // MS-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8}
 // MS-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8}