Index: docs/ControlFlowIntegrity.rst =================================================================== --- docs/ControlFlowIntegrity.rst +++ docs/ControlFlowIntegrity.rst @@ -123,6 +123,54 @@ most compilers and should not have security implications, so we allow it by default. It can be disabled with ``-fsanitize=cfi-cast-strict``. +Indirect Function Call Checking +------------------------------- + +This scheme checks that function calls take place using a function of the +correct dynamic type; that is, the dynamic type of the function must match +the static type used at the call. This CFI scheme can be enabled on its own +using ``-fsanitize=cfi-icall``. + +For this scheme to work, each indirect function call in the program, other +than calls in :ref:`blacklisted ` functions, must call a +function which was either compiled with ``-fsanitize=cfi-icall`` enabled, +or whose address was taken by a function in a translation unit compiled with +``-fsanitize=cfi-icall``. + +If a function in a translation unit compiled with ``-fsanitize=cfi-icall`` +takes the address of a function not compiled with ``-fsanitize=cfi-icall``, +that address may differ from the address taken by a function in a translation +unit not compiled with ``-fsanitize=cfi-icall``. This is technically a +violation of the C and C++ standards, but it should not affect most programs. + +Each translation unit compiled with ``-fsanitize=cfi-icall`` must be +statically linked into the program or shared library, and calls across +shared library boundaries are handled as if the callee was not compiled with +``-fsanitize=cfi-icall``. + +This scheme is currently only supported on the x86 and x86_64 architectures. + +``-fsanitize=cfi-icall`` and ``-fsanitize=function`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This tool is similar to ``-fsanitize=function`` in that both tools check +the types of function calls. However, the two tools occupy different points +on the design space; ``-fsanitize=function`` is a developer tool designed +to find bugs in local development builds, whereas ``-fsanitize=cfi-icall`` +is a security hardening mechanism designed to be deployed in release builds. + +``-fsanitize=function`` has a higher space and time overhead due to a more +complex type check at indirect call sites, as well as a need for run-time +type information (RTTI), which may make it unsuitable for deployment. Because +of the need for RTTI, ``-fsanitize=function`` can only be used with C++ +programs, whereas ``-fsanitize=cfi-icall`` can protect both C and C++ programs. + +On the other hand, ``-fsanitize=function`` conforms more closely with the C++ +standard and user expectations around interaction with shared libraries; +the identity of function pointers is maintained, and calls across shared +library boundaries are no different from calls within a single program or +shared library. + .. _cfi-blacklist: Blacklist Index: include/clang/AST/Mangle.h =================================================================== --- include/clang/AST/Mangle.h +++ include/clang/AST/Mangle.h @@ -144,9 +144,6 @@ /// across translation units so it can be used with LTO. virtual void mangleTypeName(QualType T, raw_ostream &) = 0; - virtual void mangleCXXVTableBitSet(const CXXRecordDecl *RD, - raw_ostream &) = 0; - /// @} }; Index: include/clang/Basic/Sanitizers.def =================================================================== --- include/clang/Basic/Sanitizers.def +++ include/clang/Basic/Sanitizers.def @@ -84,11 +84,13 @@ // Control Flow Integrity SANITIZER("cfi-cast-strict", CFICastStrict) SANITIZER("cfi-derived-cast", CFIDerivedCast) +SANITIZER("cfi-icall", CFIICall) SANITIZER("cfi-unrelated-cast", CFIUnrelatedCast) SANITIZER("cfi-nvcall", CFINVCall) SANITIZER("cfi-vcall", CFIVCall) SANITIZER_GROUP("cfi", CFI, - CFIDerivedCast | CFIUnrelatedCast | CFINVCall | CFIVCall) + CFIDerivedCast | CFIICall | CFIUnrelatedCast | CFINVCall | + CFIVCall) // Safe Stack SANITIZER("safe-stack", SafeStack) Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -174,8 +174,6 @@ void mangleStringLiteral(const StringLiteral *, raw_ostream &) override; - void mangleCXXVTableBitSet(const CXXRecordDecl *RD, raw_ostream &) override; - bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) { // Lambda closure types are already numbered. if (isLambda(ND)) @@ -4098,21 +4096,6 @@ mangleCXXRTTIName(Ty, Out); } -void ItaniumMangleContextImpl::mangleCXXVTableBitSet(const CXXRecordDecl *RD, - raw_ostream &Out) { - if (!RD->isExternallyVisible()) { - // This part of the identifier needs to be unique across all translation - // units in the linked program. The scheme fails if multiple translation - // units are compiled using the same relative source file path, or if - // multiple translation units are built from the same source file. - SourceManager &SM = getASTContext().getSourceManager(); - Out << "[" << SM.getFileEntryForID(SM.getMainFileID())->getName() << "]"; - } - - CXXNameMangler Mangler(*this, Out); - Mangler.mangleType(QualType(RD->getTypeForDecl(), 0)); -} - void ItaniumMangleContextImpl::mangleStringLiteral(const StringLiteral *, raw_ostream &) { llvm_unreachable("Can't mangle string literals"); } Index: lib/AST/MicrosoftMangle.cpp =================================================================== --- lib/AST/MicrosoftMangle.cpp +++ lib/AST/MicrosoftMangle.cpp @@ -160,8 +160,6 @@ void mangleSEHFinallyBlock(const NamedDecl *EnclosingDecl, raw_ostream &Out) override; void mangleStringLiteral(const StringLiteral *SL, raw_ostream &Out) override; - void mangleCXXVTableBitSet(const CXXRecordDecl *RD, - raw_ostream &Out) override; bool getNextDiscriminator(const NamedDecl *ND, unsigned &disc) { // Lambda closure types are already numbered. if (isLambda(ND)) @@ -2792,21 +2790,6 @@ Mangler.getStream() << '@'; } -void MicrosoftMangleContextImpl::mangleCXXVTableBitSet(const CXXRecordDecl *RD, - raw_ostream &Out) { - if (!RD->isExternallyVisible()) { - // This part of the identifier needs to be unique across all translation - // units in the linked program. The scheme fails if multiple translation - // units are compiled using the same relative source file path, or if - // multiple translation units are built from the same source file. - SourceManager &SM = getASTContext().getSourceManager(); - Out << "[" << SM.getFileEntryForID(SM.getMainFileID())->getName() << "]"; - } - - MicrosoftCXXNameMangler mangler(*this, Out); - mangler.mangleName(RD); -} - MicrosoftMangleContext * MicrosoftMangleContext::create(ASTContext &Context, DiagnosticsEngine &Diags) { return new MicrosoftMangleContextImpl(Context, Diags); Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -2230,12 +2230,9 @@ SanitizerScope SanScope(this); - std::string OutName; - llvm::raw_string_ostream Out(OutName); - CGM.getCXXABI().getMangleContext().mangleCXXVTableBitSet(RD, Out); - llvm::Value *BitSetName = llvm::MetadataAsValue::get( - getLLVMContext(), llvm::MDString::get(getLLVMContext(), Out.str())); + getLLVMContext(), + CGM.CreateBitSetIdentifierForType(QualType(RD->getTypeForDecl(), 0))); llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy); llvm::Value *BitSetTest = Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -3383,6 +3383,29 @@ } } + // If we are checking indirect calls and this call is indirect, check that the + // function pointer is a member of the bit set for the function type. + if (SanOpts.has(SanitizerKind::CFIICall) && + (!TargetDecl || !isa(TargetDecl))) { + SanitizerScope SanScope(this); + + llvm::Value *BitSetName = llvm::MetadataAsValue::get( + getLLVMContext(), + CGM.CreateBitSetIdentifierForType(QualType(FnType, 0))); + + llvm::Value *CastedCallee = Builder.CreateBitCast(Callee, Int8PtrTy); + llvm::Value *BitSetTest = + Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::bitset_test), + {CastedCallee, BitSetName}); + + llvm::Constant *StaticData[] = { + EmitCheckSourceLocation(E->getLocStart()), + EmitCheckTypeDescriptor(QualType(FnType, 0)), + }; + EmitCheck(std::make_pair(BitSetTest, SanitizerKind::CFIICall), + "cfi_bad_icall", StaticData, CastedCallee); + } + CallArgList Args; if (Chain) Args.add(RValue::get(Builder.CreateBitCast(Chain, CGM.VoidPtrTy)), Index: lib/CodeGen/CGVTables.cpp =================================================================== --- lib/CodeGen/CGVTables.cpp +++ lib/CodeGen/CGVTables.cpp @@ -881,41 +881,45 @@ CharUnits PointerWidth = Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0)); - std::vector BitsetEntries; + typedef std::pair BSEntry; + std::vector BitsetEntries; // Create a bit set entry for each address point. for (auto &&AP : VTLayout.getAddressPoints()) { if (IsCFIBlacklistedRecord(AP.first.getBase())) continue; - BitsetEntries.push_back(CreateVTableBitSetEntry( - VTable, PointerWidth * AP.second, AP.first.getBase())); + BitsetEntries.push_back(std::make_pair(AP.first.getBase(), AP.second)); } // Sort the bit set entries for determinism. - std::sort(BitsetEntries.begin(), BitsetEntries.end(), [](llvm::MDTuple *T1, - llvm::MDTuple *T2) { - if (T1 == T2) + std::sort(BitsetEntries.begin(), BitsetEntries.end(), + [this](const BSEntry &E1, const BSEntry &E2) { + if (&E1 == &E2) return false; - StringRef S1 = cast(T1->getOperand(0))->getString(); - StringRef S2 = cast(T2->getOperand(0))->getString(); + std::string S1; + llvm::raw_string_ostream O1(S1); + getCXXABI().getMangleContext().mangleTypeName( + QualType(E1.first->getTypeForDecl(), 0), O1); + O1.flush(); + + std::string S2; + llvm::raw_string_ostream O2(S2); + getCXXABI().getMangleContext().mangleTypeName( + QualType(E2.first->getTypeForDecl(), 0), O2); + O2.flush(); + if (S1 < S2) return true; if (S1 != S2) return false; - uint64_t Offset1 = cast( - cast(T1->getOperand(2)) - ->getValue())->getZExtValue(); - uint64_t Offset2 = cast( - cast(T2->getOperand(2)) - ->getValue())->getZExtValue(); - assert(Offset1 != Offset2); - return Offset1 < Offset2; + return E1.second < E2.second; }); llvm::NamedMDNode *BitsetsMD = getModule().getOrInsertNamedMetadata("llvm.bitsets"); for (auto BitsetEntry : BitsetEntries) - BitsetsMD->addOperand(BitsetEntry); + BitsetsMD->addOperand(CreateVTableBitSetEntry( + VTable, PointerWidth * BitsetEntry.second, BitsetEntry.first)); } Index: lib/CodeGen/CodeGenModule.h =================================================================== --- lib/CodeGen/CodeGenModule.h +++ lib/CodeGen/CodeGenModule.h @@ -490,6 +490,11 @@ llvm::DenseMap DeferredEmptyCoverageMappingDecls; std::unique_ptr CoverageMapping; + + /// Mapping from canonical types to bitset identifiers. We need to maintain + /// this mapping because identifiers may be formed from distinct MDNodes. + llvm::DenseMap BitSetIdMap; + public: CodeGenModule(ASTContext &C, const HeaderSearchOptions &headersearchopts, const PreprocessorOptions &ppopts, @@ -1134,6 +1139,12 @@ void EmitVTableBitSetEntries(llvm::GlobalVariable *VTable, const VTableLayout &VTLayout); + /// Create a bitset metadata identifier (the first element of a bitset + /// metadata entry) for the given entry. This may either be an MDString + /// (for external identifiers) or a distinct unnamed MDNode (for internal + /// identifiers). + llvm::Metadata *CreateBitSetIdentifierForType(QualType T); + /// Create a bitset entry for the given vtable. llvm::MDTuple *CreateVTableBitSetEntry(llvm::GlobalVariable *VTable, CharUnits Offset, Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -917,6 +917,20 @@ if (FD->isReplaceableGlobalAllocationFunction()) F->addAttribute(llvm::AttributeSet::FunctionIndex, llvm::Attribute::NoBuiltin); + + // If we are checking indirect calls and this is not a non-static member + // function, emit a bit set entry for the function type. + if (LangOpts.Sanitize.has(SanitizerKind::CFIICall) && + !(isa(FD) && !cast(FD)->isStatic())) { + llvm::NamedMDNode *BitsetsMD = + getModule().getOrInsertNamedMetadata("llvm.bitsets"); + + llvm::Metadata *BitsetOps[] = { + CreateBitSetIdentifierForType(FD->getType()), + llvm::ConstantAsMetadata::get(F), + llvm::ConstantAsMetadata::get(llvm::ConstantInt::get(Int64Ty, 0))}; + BitsetsMD->addOperand(llvm::MDTuple::get(getLLVMContext(), BitsetOps)); + } } void CodeGenModule::addUsedGlobal(llvm::GlobalValue *GV) { @@ -3676,14 +3690,29 @@ } } +llvm::Metadata *CodeGenModule::CreateBitSetIdentifierForType(QualType T) { + llvm::Metadata *&InternalId = BitSetIdMap[T.getCanonicalType()]; + if (InternalId) + return InternalId; + + if (isExternallyVisible(T->getLinkage())) { + std::string OutName; + llvm::raw_string_ostream Out(OutName); + getCXXABI().getMangleContext().mangleTypeName(T, Out); + + InternalId = llvm::MDString::get(getLLVMContext(), Out.str()); + } else { + InternalId = llvm::MDNode::getDistinct(getLLVMContext(), + llvm::ArrayRef()); + } + + return InternalId; +} + llvm::MDTuple *CodeGenModule::CreateVTableBitSetEntry( llvm::GlobalVariable *VTable, CharUnits Offset, const CXXRecordDecl *RD) { - std::string OutName; - llvm::raw_string_ostream Out(OutName); - getCXXABI().getMangleContext().mangleCXXVTableBitSet(RD, Out); - llvm::Metadata *BitsetOps[] = { - llvm::MDString::get(getLLVMContext(), Out.str()), + CreateBitSetIdentifierForType(QualType(RD->getTypeForDecl(), 0)), llvm::ConstantAsMetadata::get(VTable), llvm::ConstantAsMetadata::get( llvm::ConstantInt::get(Int64Ty, Offset.getQuantity()))}; Index: lib/Driver/ToolChain.cpp =================================================================== --- lib/Driver/ToolChain.cpp +++ lib/Driver/ToolChain.cpp @@ -492,6 +492,6 @@ // Return sanitizers which don't require runtime support and are not // platform or architecture-dependent. using namespace SanitizerKind; - return (Undefined & ~Vptr & ~Function) | CFI | CFICastStrict | + return (Undefined & ~Vptr & ~Function) | (CFI & ~CFIICall) | CFICastStrict | UnsignedIntegerOverflow | LocalBounds; } Index: lib/Driver/ToolChains.cpp =================================================================== --- lib/Driver/ToolChains.cpp +++ lib/Driver/ToolChains.cpp @@ -3633,6 +3633,7 @@ if (IsX86_64 || IsMIPS64 || IsPowerPC64) Res |= SanitizerKind::Memory; if (IsX86 || IsX86_64) { + Res |= SanitizerKind::CFIICall; Res |= SanitizerKind::Function; Res |= SanitizerKind::SafeStack; } Index: test/CodeGen/cfi-icall.c =================================================================== --- /dev/null +++ test/CodeGen/cfi-icall.c @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s | FileCheck --check-prefix=ITANIUM %s +// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s | FileCheck --check-prefix=MS %s + +void f() { +} + +void xf(); + +void g(int b) { + void (*fp)() = b ? f : xf; + // ITANIUM: call i1 @llvm.bitset.test(i8* {{.*}}, metadata !"_ZTSFvE") + fp(); +} + +// ITANIUM-DAG: !{!"_ZTSFvE", void ()* @f, i64 0} +// ITANIUM-DAG: !{!"_ZTSFvE", void (...)* @xf, i64 0} +// MS-DAG: !{!"?6AX@Z", void ()* @f, i64 0} +// MS-DAG: !{!"?6AX@Z", void (...)* @xf, i64 0} Index: test/CodeGenCXX/cfi-cast.cpp =================================================================== --- test/CodeGenCXX/cfi-cast.cpp +++ test/CodeGenCXX/cfi-cast.cpp @@ -18,7 +18,7 @@ // CHECK-DCAST-LABEL: define void @_Z3abpP1A void abp(A *a) { - // CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") // CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-DCAST: [[TRAPBB]] @@ -32,7 +32,7 @@ // CHECK-DCAST-LABEL: define void @_Z3abrR1A void abr(A &a) { - // CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") // CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-DCAST: [[TRAPBB]] @@ -46,7 +46,7 @@ // CHECK-DCAST-LABEL: define void @_Z4abrrO1A void abrr(A &&a) { - // CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // CHECK-DCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") // CHECK-DCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-DCAST: [[TRAPBB]] @@ -60,7 +60,7 @@ // CHECK-UCAST-LABEL: define void @_Z3vbpPv void vbp(void *p) { - // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") // CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-UCAST: [[TRAPBB]] @@ -74,7 +74,7 @@ // CHECK-UCAST-LABEL: define void @_Z3vbrRc void vbr(char &r) { - // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") // CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-UCAST: [[TRAPBB]] @@ -88,7 +88,7 @@ // CHECK-UCAST-LABEL: define void @_Z4vbrrOc void vbrr(char &&r) { - // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") // CHECK-UCAST-NEXT: br i1 [[P]], label %[[CONTBB:[^ ]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-UCAST: [[TRAPBB]] @@ -103,7 +103,7 @@ // CHECK-UCAST-LABEL: define void @_Z3vcpPv // CHECK-UCAST-STRICT-LABEL: define void @_Z3vcpPv void vcp(void *p) { - // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A") - // CHECK-UCAST-STRICT: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1C") + // CHECK-UCAST: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A") + // CHECK-UCAST-STRICT: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C") static_cast(p); } Index: test/CodeGenCXX/cfi-icall.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/cfi-icall.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ITANIUM %s +// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fsanitize=cfi-icall -fsanitize-trap=cfi-icall -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=MS %s + +namespace { + +struct S {}; + +void f(S *s) { +} + +} + +void g() { + void (*fp)(S *) = f; + // CHECK: call i1 @llvm.bitset.test(i8* {{.*}}, metadata ![[VOIDS:[0-9]+]]) + fp(0); +} + +// ITANIUM: !{![[VOIDS]], void (%"struct.(anonymous namespace)::S"*)* @_ZN12_GLOBAL__N_11fEPNS_1SE, i64 0} +// MS: !{![[VOIDS]], void (%"struct.(anonymous namespace)::S"*)* @"\01?f@?A@@YAXPEAUS@?A@@@Z", i64 0} Index: test/CodeGenCXX/cfi-ms-rtti.cpp =================================================================== --- test/CodeGenCXX/cfi-ms-rtti.cpp +++ test/CodeGenCXX/cfi-ms-rtti.cpp @@ -8,5 +8,5 @@ A::A() {} -// RTTI: !{!"A@@", [2 x i8*]* {{.*}}, i64 8} -// NO-RTTI: !{!"A@@", [1 x i8*]* {{.*}}, i64 0} +// RTTI: !{!"?AUA@@", [2 x i8*]* {{.*}}, i64 8} +// NO-RTTI: !{!"?AUA@@", [1 x i8*]* {{.*}}, i64 0} Index: test/CodeGenCXX/cfi-nvcall.cpp =================================================================== --- test/CodeGenCXX/cfi-nvcall.cpp +++ test/CodeGenCXX/cfi-nvcall.cpp @@ -17,8 +17,8 @@ // CHECK-LABEL: @bg // CHECK-STRICT-LABEL: @bg extern "C" void bg(B *b) { - // CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") - // CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") + // CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") + // CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") b->g(); } @@ -29,7 +29,7 @@ // In this case C's layout is the same as its base class, so we allow // c to be of type A in non-strict mode. - // CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1A") - // CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1C") + // CHECK: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1A") + // CHECK-STRICT: call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1C") c->g(); } Index: test/CodeGenCXX/cfi-vcall.cpp =================================================================== --- test/CodeGenCXX/cfi-vcall.cpp +++ test/CodeGenCXX/cfi-vcall.cpp @@ -60,8 +60,8 @@ // ITANIUM: define void @_Z2afP1A // MS: define void @"\01?af@@YAXPEAUA@@@Z" void af(A *a) { - // ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"1A") - // MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"A@@") + // ITANIUM: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"_ZTS1A") + // MS: [[P:%[^ ]*]] = call i1 @llvm.bitset.test(i8* [[VT:%[^ ]*]], metadata !"?AUA@@") // CHECK-NEXT: br i1 [[P]], label %[[CONTBB:[^ ,]*]], label %[[TRAPBB:[^ ,]*]] // CHECK-NEXT: {{^$}} @@ -82,24 +82,24 @@ // ITANIUM: define internal void @_Z3df1PN12_GLOBAL__N_11DE // MS: define internal void @"\01?df1@@YAXPEAUD@?A@@@Z" void df1(D *d) { - // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE") - // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"A@@") + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]]) + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUA@@") d->f(); } // ITANIUM: define internal void @_Z3dg1PN12_GLOBAL__N_11DE // MS: define internal void @"\01?dg1@@YAXPEAUD@?A@@@Z" void dg1(D *d) { - // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"1B") - // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"B@@") + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTS1B") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUB@@") d->g(); } // ITANIUM: define internal void @_Z3dh1PN12_GLOBAL__N_11DE // MS: define internal void @"\01?dh1@@YAXPEAUD@?A@@@Z" void dh1(D *d) { - // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE") - // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"[{{.*}}cfi-vcall.cpp]D@?A@@") + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE]]) + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]]) d->h(); } @@ -150,8 +150,8 @@ // ITANIUM: define void @_ZN5test21fEPNS_1DE // MS: define void @"\01?f@test2@@YAXPEAUD@1@@Z" void f(D *d) { - // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"N5test21DE") - // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"A@test2@@") + // ITANIUM: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"_ZTSN5test21DE") + // MS: {{%[^ ]*}} = call i1 @llvm.bitset.test(i8* {{%[^ ]*}}, metadata !"?AUA@test2@@") d->m_fn1(); } @@ -161,28 +161,28 @@ // MS: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){8}]]} // ITANIUM: !llvm.bitsets = !{[[X:[^,]*(,[^,]*){14}]]} -// ITANIUM-DAG: !{!"1A", [3 x i8*]* @_ZTV1A, i64 16} -// ITANIUM-DAG: !{!"1A", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// ITANIUM-DAG: !{!"1B", [7 x i8*]* @_ZTCN12_GLOBAL__N_11DE0_1B, i64 32} -// ITANIUM-DAG: !{!"1A", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 64} -// ITANIUM-DAG: !{!"1C", [9 x i8*]* @_ZTCN12_GLOBAL__N_11DE8_1C, i64 32} -// ITANIUM-DAG: !{!"1A", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DAG: !{!"1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DAG: !{!"1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} -// ITANIUM-DAG: !{!"[{{.*}}cfi-vcall.cpp]N12_GLOBAL__N_11DE", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} -// ITANIUM-DAG: !{!"1A", [7 x i8*]* @_ZTV1B, i64 32} -// ITANIUM-DAG: !{!"1B", [7 x i8*]* @_ZTV1B, i64 32} -// ITANIUM-DAG: !{!"1A", [5 x i8*]* @_ZTV1C, i64 32} -// ITANIUM-DAG: !{!"1C", [5 x i8*]* @_ZTV1C, i64 32} -// ITANIUM-DAG: !{!"1A", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} -// ITANIUM-DAG: !{!"[{{.*}}cfi-vcall.cpp]Z3foovE2FA", [3 x i8*]* @_ZTVZ3foovE2FA, i64 16} - -// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTA]], i64 8} -// MS-DAG: !{!"B@@", [3 x i8*]* @[[VTB]], i64 8} -// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTAinB]], i64 8} -// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTAinC]], i64 8} -// MS-DAG: !{!"B@@", [3 x i8*]* @[[VTBinD]], i64 8} -// MS-DAG: !{!"[{{.*}}cfi-vcall.cpp]D@?A@@", [3 x i8*]* @[[VTBinD]], i64 8} -// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTAinBinD]], i64 8} -// MS-DAG: !{!"A@@", [2 x i8*]* @[[VTFA]], i64 8} -// MS-DAG: !{!"[{{.*}}cfi-vcall.cpp]FA@?1??foo@@YAXXZ@", [2 x i8*]* @[[VTFA]], i64 8} +// ITANIUM-DAG: !{!"_ZTS1A", [3 x i8*]* @_ZTV1A, i64 16} +// ITANIUM-DAG: !{!"_ZTS1A", [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: !{!"_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: !{!"_ZTS1B", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"_ZTS1C", [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 88} +// ITANIUM-DAG: !{![[DTYPE]], [12 x i8*]* @_ZTVN12_GLOBAL__N_11DE, i64 32} +// ITANIUM-DAG: !{!"_ZTS1A", [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: !{!"_ZTS1C", [5 x i8*]* @_ZTV1C, i64 32} +// ITANIUM-DAG: !{!"_ZTS1A", [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: !{!"?AUB@@", [3 x i8*]* @[[VTB]], i64 8} +// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinB]], i64 8} +// MS-DAG: !{!"?AUA@@", [2 x i8*]* @[[VTAinC]], i64 8} +// MS-DAG: !{!"?AUB@@", [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*]* @[[VTFA]], i64 8} +// MS-DAG: !{!{{[0-9]+}}, [2 x i8*]* @[[VTFA]], i64 8} Index: test/Driver/fsanitize.c =================================================================== --- test/Driver/fsanitize.c +++ test/Driver/fsanitize.c @@ -234,7 +234,7 @@ // RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi-unrelated-cast -flto -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-UCAST // RUN: %clang -target x86_64-linux-gnu -flto -fsanitize=cfi-nvcall -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NVCALL // RUN: %clang -target x86_64-linux-gnu -flto -fsanitize=cfi-vcall -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-VCALL -// CHECK-CFI: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall +// CHECK-CFI: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast,cfi-icall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall // CHECK-CFI-DCAST: -emit-llvm-bc{{.*}}-fsanitize=cfi-derived-cast // CHECK-CFI-UCAST: -emit-llvm-bc{{.*}}-fsanitize=cfi-unrelated-cast // CHECK-CFI-NVCALL: -emit-llvm-bc{{.*}}-fsanitize=cfi-nvcall @@ -243,6 +243,9 @@ // RUN: %clang -target x86_64-linux-gnu -flto -fsanitize=cfi-derived-cast -fno-lto -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NOLTO // CHECK-CFI-NOLTO: '-fsanitize=cfi-derived-cast' only allowed with '-flto' +// RUN: %clang -target mips-unknown-linux -fsanitize=cfi-icall %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-ICALL-MIPS +// CHECK-CFI-ICALL-MIPS: unsupported option '-fsanitize=cfi-icall' for target 'mips-unknown-linux' + // RUN: %clang -target x86_64-linux-gnu -fsanitize-trap=address -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-TRAP // CHECK-ASAN-TRAP: error: unsupported argument 'address' to option '-fsanitize-trap'