Index: cfe/trunk/docs/UsersManual.rst =================================================================== --- cfe/trunk/docs/UsersManual.rst +++ cfe/trunk/docs/UsersManual.rst @@ -1108,10 +1108,12 @@ the binary size increase caused by the sanitizer runtime is a concern. This flag is only compatible with ``local-bounds``, - ``unsigned-integer-overflow`` and sanitizers in the ``undefined`` - group other than ``vptr``. If this flag is supplied together with - ``-fsanitize=undefined``, the ``vptr`` sanitizer will be implicitly - disabled. + ``unsigned-integer-overflow``, sanitizers in the ``cfi`` group and + sanitizers in the ``undefined`` group other than ``vptr``. If this flag + is supplied together with ``-fsanitize=undefined``, the ``vptr`` sanitizer + will be implicitly disabled. + + This flag is enabled by default for sanitizers in the ``cfi`` group. **-f[no-]sanitize-coverage=[type,features,...]** Index: cfe/trunk/lib/CodeGen/CGCXXABI.h =================================================================== --- cfe/trunk/lib/CodeGen/CGCXXABI.h +++ cfe/trunk/lib/CodeGen/CGCXXABI.h @@ -366,7 +366,8 @@ virtual llvm::Value *getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD, llvm::Value *This, - llvm::Type *Ty) = 0; + llvm::Type *Ty, + SourceLocation Loc) = 0; /// Emit the ABI-specific virtual destructor call. virtual llvm::Value * Index: cfe/trunk/lib/CodeGen/CGClass.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGClass.cpp +++ cfe/trunk/lib/CodeGen/CGClass.cpp @@ -2134,17 +2134,21 @@ } void CodeGenFunction::EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, - llvm::Value *VTable) { + llvm::Value *VTable, + CFITypeCheckKind TCK, + SourceLocation Loc) { const CXXRecordDecl *ClassDecl = MD->getParent(); if (!SanOpts.has(SanitizerKind::CFICastStrict)) ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl); - EmitVTablePtrCheck(ClassDecl, VTable); + EmitVTablePtrCheck(ClassDecl, VTable, TCK, Loc); } void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T, llvm::Value *Derived, - bool MayBeNull) { + bool MayBeNull, + CFITypeCheckKind TCK, + SourceLocation Loc) { if (!getLangOpts().CPlusPlus) return; @@ -2184,7 +2188,7 @@ } llvm::Value *VTable = GetVTablePtr(Derived, Int8PtrTy); - EmitVTablePtrCheck(ClassDecl, VTable); + EmitVTablePtrCheck(ClassDecl, VTable, TCK, Loc); if (MayBeNull) { Builder.CreateBr(ContBlock); @@ -2193,11 +2197,15 @@ } void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD, - llvm::Value *VTable) { + llvm::Value *VTable, + CFITypeCheckKind TCK, + SourceLocation Loc) { // FIXME: Add blacklisting scheme. if (RD->isInStdNamespace()) return; + SanitizerScope SanScope(this); + std::string OutName; llvm::raw_string_ostream Out(OutName); CGM.getCXXABI().getMangleContext().mangleCXXVTableBitSet(RD, Out); @@ -2205,20 +2213,34 @@ llvm::Value *BitSetName = llvm::MetadataAsValue::get( getLLVMContext(), llvm::MDString::get(getLLVMContext(), Out.str())); - llvm::Value *BitSetTest = Builder.CreateCall( - CGM.getIntrinsic(llvm::Intrinsic::bitset_test), - {Builder.CreateBitCast(VTable, CGM.Int8PtrTy), BitSetName}); - - llvm::BasicBlock *ContBlock = createBasicBlock("vtable.check.cont"); - llvm::BasicBlock *TrapBlock = createBasicBlock("vtable.check.trap"); - - Builder.CreateCondBr(BitSetTest, ContBlock, TrapBlock); - - EmitBlock(TrapBlock); - Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::trap), {}); - Builder.CreateUnreachable(); + llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy); + llvm::Value *BitSetTest = + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test), + {CastedVTable, BitSetName}); + + SanitizerMask M; + switch (TCK) { + case CFITCK_VCall: + M = SanitizerKind::CFIVCall; + break; + case CFITCK_NVCall: + M = SanitizerKind::CFINVCall; + break; + case CFITCK_DerivedCast: + M = SanitizerKind::CFIDerivedCast; + break; + case CFITCK_UnrelatedCast: + M = SanitizerKind::CFIUnrelatedCast; + break; + } - EmitBlock(ContBlock); + llvm::Constant *StaticData[] = { + EmitCheckSourceLocation(Loc), + EmitCheckTypeDescriptor(QualType(RD->getTypeForDecl(), 0)), + llvm::ConstantInt::get(Int8Ty, TCK), + }; + EmitCheck(std::make_pair(BitSetTest, M), "cfi_bad_type", StaticData, + CastedVTable); } // FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do Index: cfe/trunk/lib/CodeGen/CGExpr.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGExpr.cpp +++ cfe/trunk/lib/CodeGen/CGExpr.cpp @@ -3035,7 +3035,8 @@ Derived, E->getType()); if (SanOpts.has(SanitizerKind::CFIDerivedCast)) - EmitVTablePtrCheckForCast(E->getType(), Derived, /*MayBeNull=*/false); + EmitVTablePtrCheckForCast(E->getType(), Derived, /*MayBeNull=*/false, + CFITCK_DerivedCast, E->getLocStart()); return MakeAddrLValue(Derived, E->getType()); } @@ -3048,7 +3049,8 @@ ConvertType(CE->getTypeAsWritten())); if (SanOpts.has(SanitizerKind::CFIUnrelatedCast)) - EmitVTablePtrCheckForCast(E->getType(), V, /*MayBeNull=*/false); + EmitVTablePtrCheckForCast(E->getType(), V, /*MayBeNull=*/false, + CFITCK_UnrelatedCast, E->getLocStart()); return MakeAddrLValue(V, E->getType()); } Index: cfe/trunk/lib/CodeGen/CGExprCXX.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGExprCXX.cpp +++ cfe/trunk/lib/CodeGen/CGExprCXX.cpp @@ -254,12 +254,13 @@ if (const CXXConstructorDecl *Ctor = dyn_cast(MD)) { Callee = CGM.GetAddrOfFunction(GlobalDecl(Ctor, Ctor_Complete), Ty); } else if (UseVirtualCall) { - Callee = CGM.getCXXABI().getVirtualFunctionPointer(*this, MD, This, Ty); + Callee = CGM.getCXXABI().getVirtualFunctionPointer(*this, MD, This, Ty, + CE->getLocStart()); } else { if (SanOpts.has(SanitizerKind::CFINVCall) && MD->getParent()->isDynamicClass()) { llvm::Value *VTable = GetVTablePtr(This, Int8PtrTy); - EmitVTablePtrCheckForCall(MD, VTable); + EmitVTablePtrCheckForCall(MD, VTable, CFITCK_NVCall, CE->getLocStart()); } if (getLangOpts().AppleKext && MD->isVirtual() && HasQualifier) Index: cfe/trunk/lib/CodeGen/CGExprScalar.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGExprScalar.cpp +++ cfe/trunk/lib/CodeGen/CGExprScalar.cpp @@ -1386,7 +1386,9 @@ if (CGF.SanOpts.has(SanitizerKind::CFIUnrelatedCast)) { if (auto PT = DestTy->getAs()) CGF.EmitVTablePtrCheckForCast(PT->getPointeeType(), Src, - /*MayBeNull=*/true); + /*MayBeNull=*/true, + CodeGenFunction::CFITCK_UnrelatedCast, + CE->getLocStart()); } return Builder.CreateBitCast(Src, DstTy); @@ -1420,7 +1422,9 @@ if (CGF.SanOpts.has(SanitizerKind::CFIDerivedCast)) CGF.EmitVTablePtrCheckForCast(DestTy->getPointeeType(), Derived, - /*MayBeNull=*/true); + /*MayBeNull=*/true, + CodeGenFunction::CFITCK_DerivedCast, + CE->getLocStart()); return Derived; } Index: cfe/trunk/lib/CodeGen/CodeGenFunction.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.h +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h @@ -1311,19 +1311,29 @@ /// to by This. llvm::Value *GetVTablePtr(llvm::Value *This, llvm::Type *Ty); + enum CFITypeCheckKind { + CFITCK_VCall, + CFITCK_NVCall, + CFITCK_DerivedCast, + CFITCK_UnrelatedCast, + }; + /// \brief Derived is the presumed address of an object of type T after a /// cast. If T is a polymorphic class type, emit a check that the virtual /// table for Derived belongs to a class derived from T. void EmitVTablePtrCheckForCast(QualType T, llvm::Value *Derived, - bool MayBeNull); + bool MayBeNull, CFITypeCheckKind TCK, + SourceLocation Loc); /// EmitVTablePtrCheckForCall - Virtual method MD is being called via VTable. /// If vptr CFI is enabled, emit a check that VTable is valid. - void EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, llvm::Value *VTable); + void EmitVTablePtrCheckForCall(const CXXMethodDecl *MD, llvm::Value *VTable, + CFITypeCheckKind TCK, SourceLocation Loc); /// EmitVTablePtrCheck - Emit a check that VTable is a valid virtual table for /// RD using llvm.bitset.test. - void EmitVTablePtrCheck(const CXXRecordDecl *RD, llvm::Value *VTable); + void EmitVTablePtrCheck(const CXXRecordDecl *RD, llvm::Value *VTable, + CFITypeCheckKind TCK, SourceLocation Loc); /// CanDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given /// expr can be devirtualized. Index: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp @@ -204,7 +204,8 @@ llvm::Value *getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD, llvm::Value *This, - llvm::Type *Ty) override; + llvm::Type *Ty, + SourceLocation Loc) override; llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, @@ -1439,13 +1440,15 @@ llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD, llvm::Value *This, - llvm::Type *Ty) { + llvm::Type *Ty, + SourceLocation Loc) { GD = GD.getCanonicalDecl(); Ty = Ty->getPointerTo()->getPointerTo(); llvm::Value *VTable = CGF.GetVTablePtr(This, Ty); if (CGF.SanOpts.has(SanitizerKind::CFIVCall)) - CGF.EmitVTablePtrCheckForCall(cast(GD.getDecl()), VTable); + CGF.EmitVTablePtrCheckForCall(cast(GD.getDecl()), VTable, + CodeGenFunction::CFITCK_VCall, Loc); uint64_t VTableIndex = CGM.getItaniumVTableContext().getMethodVTableIndex(GD); llvm::Value *VFuncPtr = @@ -1463,7 +1466,8 @@ Dtor, getFromDtorType(DtorType)); llvm::Type *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo); llvm::Value *Callee = - getVirtualFunctionPointer(CGF, GlobalDecl(Dtor, DtorType), This, Ty); + getVirtualFunctionPointer(CGF, GlobalDecl(Dtor, DtorType), This, Ty, + CE ? CE->getLocStart() : SourceLocation()); CGF.EmitCXXMemberOrOperatorCall(Dtor, Callee, ReturnValueSlot(), This, /*ImplicitParam=*/nullptr, QualType(), CE); Index: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp +++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp @@ -221,8 +221,8 @@ CharUnits VPtrOffset) override; llvm::Value *getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD, - llvm::Value *This, - llvm::Type *Ty) override; + llvm::Value *This, llvm::Type *Ty, + SourceLocation Loc) override; llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, @@ -1588,7 +1588,8 @@ llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF, GlobalDecl GD, llvm::Value *This, - llvm::Type *Ty) { + llvm::Type *Ty, + SourceLocation Loc) { GD = GD.getCanonicalDecl(); CGBuilderTy &Builder = CGF.Builder; @@ -1616,7 +1617,8 @@ const CGFunctionInfo *FInfo = &CGM.getTypes().arrangeCXXStructorDeclaration( Dtor, StructorType::Deleting); llvm::Type *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo); - llvm::Value *Callee = getVirtualFunctionPointer(CGF, GD, This, Ty); + llvm::Value *Callee = getVirtualFunctionPointer( + CGF, GD, This, Ty, CE ? CE->getLocStart() : SourceLocation()); ASTContext &Context = getContext(); llvm::Value *ImplicitParam = llvm::ConstantInt::get( Index: cfe/trunk/lib/Driver/SanitizerArgs.cpp =================================================================== --- cfe/trunk/lib/Driver/SanitizerArgs.cpp +++ cfe/trunk/lib/Driver/SanitizerArgs.cpp @@ -25,7 +25,7 @@ using namespace llvm::opt; enum : SanitizerMask { - NeedsUbsanRt = Undefined | Integer, + NeedsUbsanRt = Undefined | Integer | CFI, NotAllowedWithTrap = Vptr, RequiresPIE = Memory | DataFlow, NeedsUnwindTables = Address | Thread | Memory | DataFlow, @@ -35,7 +35,8 @@ LegacyFsanitizeRecoverMask = Undefined | Integer, NeedsLTO = CFI, TrappingSupported = - (Undefined & ~Vptr) | UnsignedIntegerOverflow | LocalBounds, + (Undefined & ~Vptr) | UnsignedIntegerOverflow | LocalBounds | CFI, + TrappingDefault = CFI, }; enum CoverageFeature { @@ -166,6 +167,9 @@ } } + // Apply default trapping behavior. + TrappingKinds |= TrappingDefault & ~TrapRemove; + return TrappingKinds; } Index: cfe/trunk/test/CodeGenCXX/cfi-cast.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/cfi-cast.cpp +++ cfe/trunk/test/CodeGenCXX/cfi-cast.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=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 -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 -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-UCAST-STRICT %s +// 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 // 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 @@ -19,7 +19,7 @@ // CHECK-DCAST-LABEL: define void @_Z3abpP1A void abp(A *a) { // CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") - // CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] + // CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-DCAST: [[TRAPBB]] // CHECK-DCAST-NEXT: call void @llvm.trap() @@ -33,7 +33,7 @@ // CHECK-DCAST-LABEL: define void @_Z3abrR1A void abr(A &a) { // CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") - // CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] + // CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-DCAST: [[TRAPBB]] // CHECK-DCAST-NEXT: call void @llvm.trap() @@ -47,7 +47,7 @@ // CHECK-DCAST-LABEL: define void @_Z4abrrO1A void abrr(A &&a) { // CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") - // CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] + // CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-DCAST: [[TRAPBB]] // CHECK-DCAST-NEXT: call void @llvm.trap() @@ -61,7 +61,7 @@ // CHECK-UCAST-LABEL: define void @_Z3vbpPv void vbp(void *p) { // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") - // CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] + // CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-UCAST: [[TRAPBB]] // CHECK-UCAST-NEXT: call void @llvm.trap() @@ -75,7 +75,7 @@ // CHECK-UCAST-LABEL: define void @_Z3vbrRc void vbr(char &r) { // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") - // CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] + // CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-UCAST: [[TRAPBB]] // CHECK-UCAST-NEXT: call void @llvm.trap() @@ -89,7 +89,7 @@ // CHECK-UCAST-LABEL: define void @_Z4vbrrOc void vbrr(char &&r) { // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") - // CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] + // CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-UCAST: [[TRAPBB]] // CHECK-UCAST-NEXT: call void @llvm.trap() Index: cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp +++ cfe/trunk/test/CodeGenCXX/cfi-vcall.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck %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=NDIAG %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --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=DIAG --check-prefix=DIAG-RECOVER %s struct A { A(); @@ -33,14 +35,23 @@ void D::f() { } +// DIAG: @[[SRC:.*]] = private unnamed_addr constant [{{.*}} x i8] c"{{.*}}test/CodeGenCXX/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: @[[BADTYPESTATIC:.*]] = private unnamed_addr global { { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }*, i8 } { { [{{.*}} x i8]*, i32, i32 } { [{{.*}} x i8]* @[[SRC]], i32 58, i32 3 }, { i16, i16, [4 x i8] }* @[[TYPE]], i8 0 } + // CHECK: define void @_Z2afP1A void af(A *a) { - // CHECK: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A") - // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ]*]] + // CHECK: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"1A") + // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]] // CHECK: [[TRAPBB]] - // CHECK-NEXT: call void @llvm.trap() - // CHECK-NEXT: unreachable + // NDIAG-NEXT: call void @llvm.trap() + // NDIAG-NEXT: unreachable + // DIAG-NEXT: [[VTINT:%[^ ]*]] = ptrtoint i8* [[VT]] to i64 + // DIAG-ABORT-NEXT: call void @__ubsan_handle_cfi_bad_type_abort(i8* bitcast ({{.*}} @[[BADTYPESTATIC]] to i8*), i64 [[VTINT]]) + // DIAG-ABORT-NEXT: unreachable + // DIAG-RECOVER-NEXT: call void @__ubsan_handle_cfi_bad_type(i8* bitcast ({{.*}} @[[BADTYPESTATIC]] to i8*), i64 [[VTINT]]) + // DIAG-RECOVER-NEXT: br label %[[CONTBB]] // CHECK: [[CONTBB]] // CHECK: call void %