Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -165,8 +165,8 @@ def warn_drv_enabling_rtti_with_exceptions : Warning< "implicitly enabling rtti for exception handling">, InGroup>; -def warn_drv_disabling_vptr_no_rtti_default : Warning< - "implicitly disabling vptr sanitizer because rtti wasn't enabled">, +def warn_drv_disabling_sanitizer_no_rtti_default : Warning< + "implicitly disabling %0 sanitizer because rtti wasn't enabled">, InGroup>; def note_drv_command_failed_diag_msg : Note< Index: include/clang/Basic/Sanitizers.def =================================================================== --- include/clang/Basic/Sanitizers.def +++ include/clang/Basic/Sanitizers.def @@ -80,6 +80,7 @@ // Control Flow Integrity SANITIZER("cfi-cast-strict", CFICastStrict) +SANITIZER("cfi-diag", CFIDiag) SANITIZER("cfi-derived-cast", CFIDerivedCast) SANITIZER("cfi-unrelated-cast", CFIUnrelatedCast) SANITIZER("cfi-nvcall", CFINVCall) Index: lib/CodeGen/CGCXXABI.h =================================================================== --- lib/CodeGen/CGCXXABI.h +++ 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: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ 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,7 +2197,9 @@ } void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD, - llvm::Value *VTable) { + llvm::Value *VTable, + CFITypeCheckKind TCK, + SourceLocation Loc) { // FIXME: Add blacklisting scheme. if (RD->isInStdNamespace()) return; @@ -2205,9 +2211,10 @@ 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::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy); + llvm::Value *BitSetTest = + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test), + {CastedVTable, BitSetName}); llvm::BasicBlock *ContBlock = createBasicBlock("vtable.check.cont"); llvm::BasicBlock *TrapBlock = createBasicBlock("vtable.check.trap"); @@ -2215,8 +2222,36 @@ Builder.CreateCondBr(BitSetTest, ContBlock, TrapBlock); EmitBlock(TrapBlock); - Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::trap), {}); - Builder.CreateUnreachable(); + if (SanOpts.has(SanitizerKind::CFIDiag)) { + QualType Ty(RD->getTypeForDecl(), 0); + llvm::Constant *StaticData[] = { + EmitCheckSourceLocation(Loc), + EmitCheckTypeDescriptor(Ty), + llvm::ConstantInt::get(Int8Ty, TCK), + }; + llvm::Constant *StaticDataConst = llvm::ConstantStruct::getAnon(StaticData); + auto *StaticDataGlobal = new llvm::GlobalVariable( + CGM.getModule(), StaticDataConst->getType(), false, + llvm::GlobalVariable::PrivateLinkage, StaticDataConst); + StaticDataGlobal->setUnnamedAddr(true); + CGM.getSanitizerMetadata()->disableSanitizerForGlobal(StaticDataGlobal); + + llvm::Type *ArgTypes[] = { + StaticDataGlobal->getType(), + Int8PtrTy, + }; + + llvm::FunctionType *FnType = + llvm::FunctionType::get(CGM.VoidTy, ArgTypes, false); + + llvm::Value *Fn = + CGM.CreateRuntimeFunction(FnType, "__cfi_handle_bad_type"); + EmitNounwindRuntimeCall(Fn, {StaticDataGlobal, CastedVTable}); + Builder.CreateBr(ContBlock); + } else { + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::trap), {}); + Builder.CreateUnreachable(); + } EmitBlock(ContBlock); } Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ 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: lib/CodeGen/CGExprCXX.cpp =================================================================== --- lib/CodeGen/CGExprCXX.cpp +++ 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: lib/CodeGen/CGExprScalar.cpp =================================================================== --- lib/CodeGen/CGExprScalar.cpp +++ 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: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ 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: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ 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: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ 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: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -25,7 +25,7 @@ using namespace llvm::opt; enum : SanitizerMask { - NeedsUbsanRt = Undefined | Integer, + NeedsUbsanRt = Undefined | Integer | CFIDiag, NotAllowedWithTrap = Vptr, RequiresPIE = Memory | DataFlow, NeedsUnwindTables = Address | Thread | Memory | DataFlow, @@ -189,27 +189,34 @@ } Add &= ~NotSupported; + auto diagnoseRTTIConflict = [&](SanitizerMask Mask, StringRef Name) { + if (Add & Mask && + (RTTIMode == ToolChain::RM_DisabledImplicitly || + RTTIMode == ToolChain::RM_DisabledExplicitly)) { + if (RTTIMode == ToolChain::RM_DisabledImplicitly) + // Warn about not having rtti enabled if the sanitizer is explicitly + // enabled + D.Diag(diag::warn_drv_disabling_sanitizer_no_rtti_default) << Name; + else { + const llvm::opt::Arg *NoRTTIArg = TC.getRTTIArg(); + assert(NoRTTIArg && + "RTTI disabled explicitly but we have no argument!"); + std::string FlagName = "-fsanitize="; + FlagName += Name; + D.Diag(diag::err_drv_argument_not_allowed_with) + << FlagName << NoRTTIArg->getAsString(Args); + } + + // Take out the sanitizer from the enabled sanitizers + AllRemove |= Mask; + } + }; + // Test for -fno-rtti + explicit -fsanitizer=vptr before expanding groups // so we don't error out if -fno-rtti and -fsanitize=undefined were // passed. - if (Add & Vptr && - (RTTIMode == ToolChain::RM_DisabledImplicitly || - RTTIMode == ToolChain::RM_DisabledExplicitly)) { - if (RTTIMode == ToolChain::RM_DisabledImplicitly) - // Warn about not having rtti enabled if the vptr sanitizer is - // explicitly enabled - D.Diag(diag::warn_drv_disabling_vptr_no_rtti_default); - else { - const llvm::opt::Arg *NoRTTIArg = TC.getRTTIArg(); - assert(NoRTTIArg && - "RTTI disabled explicitly but we have no argument!"); - D.Diag(diag::err_drv_argument_not_allowed_with) - << "-fsanitize=vptr" << NoRTTIArg->getAsString(Args); - } - - // Take out the Vptr sanitizer from the enabled sanitizers - AllRemove |= Vptr; - } + diagnoseRTTIConflict(Vptr, "vptr"); + diagnoseRTTIConflict(CFIDiag, "cfi-diag"); Add = expandSanitizerGroups(Add); // Group expansion may have enabled a sanitizer which is disabled later. Index: test/CodeGenCXX/cfi-vcall.cpp =================================================================== --- test/CodeGenCXX/cfi-vcall.cpp +++ test/CodeGenCXX/cfi-vcall.cpp @@ -1,4 +1,5 @@ -// 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 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NDIAG %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-vcall,cfi-diag -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=DIAG %s struct A { A(); @@ -33,14 +34,20 @@ 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 54, 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: [[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: call void @__cfi_handle_bad_type({{.*}} @[[BADTYPESTATIC]], i8* [[VT]]) + // DIAG-NEXT: br label %[[CONTBB]] // CHECK: [[CONTBB]] // CHECK: call void % Index: test/Driver/rtti-options.cpp =================================================================== --- test/Driver/rtti-options.cpp +++ test/Driver/rtti-options.cpp @@ -20,10 +20,16 @@ // RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=vptr %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-WARN %s // RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=vptr -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s // RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=vptr -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-ERROR %s +// RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=cfi-diag %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-CFI-WARN %s +// RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=cfi-diag -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s +// RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=cfi-diag -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-CFI-ERROR %s // RUN: %clang -### -c -target x86_64-scei-ps4 -fsanitize=undefined -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=vptr %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=vptr -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=vptr -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-ERROR %s +// RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=cfi-diag %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s +// RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=cfi-diag -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s +// RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=cfi-diag -fno-rtti %s 2>&1 | FileCheck -check-prefix=CHECK-SAN-CFI-ERROR %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=undefined %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s // RUN: %clang -### -c -target x86_64-unknown-unknown -fsanitize=undefined -frtti %s 2>&1 | FileCheck -check-prefix=CHECK-OK %s @@ -53,6 +59,8 @@ // CHECK-UNUSED: warning: argument unused during compilation: '-fcxx-exceptions' // CHECK-SAN-WARN: implicitly disabling vptr sanitizer because rtti wasn't enabled // CHECK-SAN-ERROR: invalid argument '-fsanitize=vptr' not allowed with '-fno-rtti' +// CHECK-SAN-CFI-WARN: implicitly disabling cfi-diag sanitizer because rtti wasn't enabled +// CHECK-SAN-CFI-ERROR: invalid argument '-fsanitize=cfi-diag' not allowed with '-fno-rtti' // CHECK-EXC-WARN: implicitly enabling rtti for exception handling // CHECK-EXC-ERROR: invalid argument '-fno-rtti' not allowed with '-fexceptions' // CHECK-EXC-ERROR-CXX: invalid argument '-fno-rtti' not allowed with '-fcxx-exceptions'