]> granicus.if.org Git - clang/commitdiff
CFI: Introduce -fsanitize=cfi-icall flag.
authorPeter Collingbourne <peter@pcc.me.uk>
Thu, 10 Sep 2015 02:17:40 +0000 (02:17 +0000)
committerPeter Collingbourne <peter@pcc.me.uk>
Thu, 10 Sep 2015 02:17:40 +0000 (02:17 +0000)
This flag causes the compiler to emit bit set entries for functions as well
as runtime bitset checks at indirect call sites. Depends on the new function
bitset mechanism.

Differential Revision: http://reviews.llvm.org/D11857

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@247238 91177308-0d34-0410-b5e6-96231b3b80d8

19 files changed:
docs/ControlFlowIntegrity.rst
docs/ControlFlowIntegrityDesign.rst
include/clang/AST/Mangle.h
include/clang/Basic/Sanitizers.def
lib/AST/ItaniumMangle.cpp
lib/AST/MicrosoftMangle.cpp
lib/CodeGen/CGClass.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CGVTables.cpp
lib/CodeGen/CodeGenModule.cpp
lib/Driver/ToolChain.cpp
lib/Driver/ToolChains.cpp
test/CodeGen/cfi-icall.c [new file with mode: 0644]
test/CodeGenCXX/cfi-cast.cpp
test/CodeGenCXX/cfi-icall.cpp [new file with mode: 0644]
test/CodeGenCXX/cfi-ms-rtti.cpp
test/CodeGenCXX/cfi-nvcall.cpp
test/CodeGenCXX/cfi-vcall.cpp
test/Driver/fsanitize.c

index b043d24a142faf04d755f7fd0895019e9e5405cf..b583fc0debcd5bc8b4113ec786d6805de3783cd8 100644 (file)
@@ -20,12 +20,14 @@ program's control flow. These schemes have been optimized for performance,
 allowing developers to enable them in release builds.
 
 To enable Clang's available CFI schemes, use the flag ``-fsanitize=cfi``.
-As currently implemented, CFI relies on link-time optimization (LTO); so it is
-required to specify ``-flto``, and the linker used must support LTO, for example
-via the `gold plugin`_. To allow the checks to be implemented efficiently,
-the program must be structured such that certain object files are compiled
-with CFI enabled, and are statically linked into the program. This may
-preclude the use of shared libraries in some cases.
+As currently implemented, all of Clang's CFI schemes (``cfi-vcall``,
+``cfi-derived-cast``, ``cfi-unrelated-cast``, ``cfi-nvcall``, ``cfi-icall``)
+rely on link-time optimization (LTO); so it is required to specify
+``-flto``, and the linker used must support LTO, for example via the `gold
+plugin`_. To allow the checks to be implemented efficiently, the program must
+be structured such that certain object files are compiled with CFI enabled,
+and are statically linked into the program. This may preclude the use of
+shared libraries in some cases.
 
 Clang currently implements forward-edge CFI for member function calls and
 bad cast checking. More schemes are under development.
@@ -123,6 +125,54 @@ member functions on class instances with specific properties that works under
 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 <cfi-blacklist>` 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 89aa038d003e07c93028ea432689a2122dfd4ec2..af3744576d71212063ca79cb1ea136732be6cbd9 100644 (file)
@@ -273,3 +273,11 @@ Eliminating Bit Vector Checks for All-Ones Bit Vectors
 If the bit vector is all ones, the bit vector check is redundant; we simply
 need to check that the address is in range and well aligned. This is more
 likely to occur if the virtual tables are padded.
+
+Forward-Edge CFI for Indirect Function Calls
+============================================
+
+Sorry, no documentation yet, but see the comments at the top of
+``LowerBitSets::buildBitSetsFromFunctions`` in `LowerBitSets.cpp`_.
+
+.. _LowerBitSets.cpp: http://llvm.org/klaus/llvm/blob/master/lib/Transforms/IPO/LowerBitSets.cpp
index 735ae11a2339ce62fd1b50700b5373167de1cf8d..7b725b65ca1e1f6c06ac011233c58e00bfadd3a3 100644 (file)
@@ -144,9 +144,6 @@ public:
   /// 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 1b528c8d6f69387f3a4572bf1292b8a2af59ee4c..4b685936291099ce3b3c5db970842a1276173942 100644 (file)
@@ -84,11 +84,13 @@ SANITIZER("dataflow", DataFlow)
 // 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 3f40743d9b4ae1692eb704aa24ae0515ca675c25..774ef9ae35208888402e507ea89bf4752c853cf9 100644 (file)
@@ -174,8 +174,6 @@ public:
 
   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 @@ void ItaniumMangleContextImpl::mangleTypeName(QualType Ty, raw_ostream &Out) {
   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 dd6e952bbae0810421efb61ddc841299485573be..fcd7a4ab1ede9518ca2cad095f76cf7fd965660b 100644 (file)
@@ -161,8 +161,6 @@ public:
   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))
@@ -2776,21 +2774,6 @@ void MicrosoftMangleContextImpl::mangleStringLiteral(const StringLiteral *SL,
   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 dc65b14a91fba888e3a10cbfe0a1a8698a0536e8..56787e5de26bf2b476e8e72edd4a89dcfe7f3a68 100644 (file)
@@ -2473,12 +2473,9 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
 
   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.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0)));
 
   llvm::Value *CastedVTable = Builder.CreateBitCast(VTable, Int8PtrTy);
   llvm::Value *BitSetTest =
index 8339444c20e656e09347785b2b2dbb20e3e175a1..5e6c4de40878068b99bae1aea7937cc91412bbab 100644 (file)
@@ -3780,6 +3780,29 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
     }
   }
 
+  // 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<FunctionDecl>(TargetDecl))) {
+    SanitizerScope SanScope(this);
+
+    llvm::Value *BitSetName = llvm::MetadataAsValue::get(
+        getLLVMContext(),
+        CGM.CreateMetadataIdentifierForType(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 6aa624ec060725381728297e3b15520cf73fffe6..4c3202ca656f18d09ae91f4caf2ee58f34125a71 100644 (file)
@@ -893,41 +893,45 @@ void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
   CharUnits PointerWidth =
       Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
 
-  std::vector<llvm::MDTuple *> BitsetEntries;
+  typedef std::pair<const CXXRecordDecl *, unsigned> BSEntry;
+  std::vector<BSEntry> 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<llvm::MDString>(T1->getOperand(0))->getString();
-    StringRef S2 = cast<llvm::MDString>(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<llvm::ConstantInt>(
-                           cast<llvm::ConstantAsMetadata>(T1->getOperand(2))
-                               ->getValue())->getZExtValue();
-    uint64_t Offset2 = cast<llvm::ConstantInt>(
-                           cast<llvm::ConstantAsMetadata>(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 a2d554bde548c5e34cc928edd36ec3277983de0c..8f0259d502a0b40133afb954d37294fba6df51f0 100644 (file)
@@ -941,6 +941,20 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F,
   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<CXXMethodDecl>(FD) && !cast<CXXMethodDecl>(FD)->isStatic())) {
+    llvm::NamedMDNode *BitsetsMD =
+        getModule().getOrInsertNamedMetadata("llvm.bitsets");
+
+    llvm::Metadata *BitsetOps[] = {
+        CreateMetadataIdentifierForType(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) {
@@ -3824,12 +3838,8 @@ llvm::Metadata *CodeGenModule::CreateMetadataIdentifierForType(QualType T) {
 
 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()),
+      CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0)),
       llvm::ConstantAsMetadata::get(VTable),
       llvm::ConstantAsMetadata::get(
           llvm::ConstantInt::get(Int64Ty, Offset.getQuantity()))};
index e034f72a26592714594964ee577709f1cac812b2..e93487d28c212c4c75fd7b824981bac726a80e90 100644 (file)
@@ -495,6 +495,6 @@ SanitizerMask ToolChain::getSupportedSanitizers() const {
   // 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 7f0b9bb7472269310ba1cd8dbcda25018e3f57ca..c3f9b5876d71fec46b41c505ef2c0d5e5f194e1d 100644 (file)
@@ -3764,6 +3764,7 @@ SanitizerMask Linux::getSupportedSanitizers() const {
   if (IsX86_64 || IsMIPS64 || IsPowerPC64)
     Res |= SanitizerKind::Memory;
   if (IsX86 || IsX86_64) {
+    Res |= SanitizerKind::CFIICall;
     Res |= SanitizerKind::Function;
     Res |= SanitizerKind::SafeStack;
   }
diff --git a/test/CodeGen/cfi-icall.c b/test/CodeGen/cfi-icall.c
new file mode 100644 (file)
index 0000000..d6cebef
--- /dev/null
@@ -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=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
+
+// Tests that we assign appropriate identifiers to unprototyped functions.
+
+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 7efcd9e0cae47aa79d5e762bcc258a71e8d0e76e..0b96cb6506c07de272a7708ab8371a8c5d3c6160 100644 (file)
@@ -18,7 +18,7 @@ struct C : A {};
 
 // 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 @@ void abp(A *a) {
 
 // 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 @@ void abr(A &a) {
 
 // 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 @@ void abrr(A &&a) {
 
 // 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 @@ void vbp(void *p) {
 
 // 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 @@ void vbr(char &r) {
 
 // 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,23 +103,23 @@ void vbrr(char &&r) {
 // 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<C*>(p);
 }
 
 // CHECK-UCAST-LABEL: define void @_Z3bcpP1B
 // CHECK-UCAST-STRICT-LABEL: define void @_Z3bcpP1B
 void bcp(B *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")
   (C *)p;
 }
 
 // CHECK-UCAST-LABEL: define void @_Z8bcp_callP1B
 // CHECK-UCAST-STRICT-LABEL: define void @_Z8bcp_callP1B
 void bcp_call(B *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")
   ((C *)p)->f();
 }
diff --git a/test/CodeGenCXX/cfi-icall.cpp b/test/CodeGenCXX/cfi-icall.cpp
new file mode 100644 (file)
index 0000000..eceb92a
--- /dev/null
@@ -0,0 +1,23 @@
+// 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
+
+// Tests that we assign unnamed metadata nodes to functions whose types have
+// internal linkage.
+
+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 5203a6bb0c8bb719f9d7265edf46cd36066650c6..b6e9175c865c6361dc4e8a80ce8e3aae62ea4d60 100644 (file)
@@ -8,5 +8,5 @@ struct A {
 
 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 b0db478c9d0cbf84cd2432d8a91283f7c294562c..be4d8448a2e0c3d3057cacb11952e18079f0f8a3 100644 (file)
@@ -17,8 +17,8 @@ struct C : A {
 // 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 @@ extern "C" void cg(C *c) {
   // 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 5cb5e02cf585084e644977a2e0b03a5c9dd38d9c..daa0531e85d6dc8ac10d4eca5f0a37c997612f1f 100644 (file)
@@ -60,8 +60,8 @@ void D::h() {
 // 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 @@ void af(A *a) {
 // 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 @@ struct D : C {
 // 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 @@ void f(D *d) {
 // 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 15b438332730a2e0ad8c2188c27482a6efc429b4..ce140e58c9630369fac73c7ed7f08da7a05900cb 100644 (file)
 // 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
 // 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'