Hi rsmith,
This causes programs compiled with this flag to print a diagnostic when
a control flow integrity check fails instead of aborting. Diagnostics are
printed using UBSan's runtime library.
The main motivation of this feature over -fsanitize=vptr is fidelity with
the -fsanitize=cfi implementation: the diagnostics are printed under exactly
the same conditions as those which would cause -fsanitize=cfi to abort the
program. This means that the same restrictions apply regarding compiling
all translation units with -fsanitize=cfi, cross-DSO virtual calls are
forbidden, etc.
http://reviews.llvm.org/D10268
Files:
include/clang/Basic/DiagnosticDriverKinds.td
include/clang/Basic/Sanitizers.def
lib/CodeGen/CGCXXABI.h
lib/CodeGen/CGClass.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CGExprCXX.cpp
lib/CodeGen/CGExprScalar.cpp
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/ItaniumCXXABI.cpp
lib/CodeGen/MicrosoftCXXABI.cpp
lib/Driver/SanitizerArgs.cpp
test/CodeGenCXX/cfi-vcall.cpp
test/Driver/rtti-options.cpp
EMAIL PREFERENCES
http://reviews.llvm.org/settings/panel/emailpreferences/
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<DiagGroup<"rtti-for-exceptions">>;
-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<DiagGroup<"auto-disable-vptr-sanitizer">>;
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,16 +2188,18 @@
}
llvm::Value *VTable = GetVTablePtr(Derived, Int8PtrTy);
- EmitVTablePtrCheck(ClassDecl, VTable);
+ EmitVTablePtrCheck(ClassDecl, VTable, TCK, Loc);
if (MayBeNull) {
Builder.CreateBr(ContBlock);
EmitBlock(ContBlock);
}
}
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,18 +2211,47 @@
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");
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<CXXConstructorDecl>(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<PointerType>())
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<CXXMethodDecl>(GD.getDecl()), VTable);
+ CGF.EmitVTablePtrCheckForCall(cast<CXXMethodDecl>(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'
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits