Index: docs/ControlFlowIntegrity.rst =================================================================== --- docs/ControlFlowIntegrity.rst +++ docs/ControlFlowIntegrity.rst @@ -41,11 +41,9 @@ This CFI scheme can be enabled on its own using ``-fsanitize=cfi-vcall``. For this scheme to work, all translation units containing the definition -of a virtual member function (whether inline or not) must be compiled -with ``-fsanitize=cfi-vcall`` enabled and be statically linked into the -program. Classes in the C++ standard library (under namespace ``std``) are -exempted from checking, and therefore programs may be linked against a -pre-built standard library, but this may change in the future. +of a virtual member function (whether inline or not), other than members +of :ref:`blacklisted ` types, must be compiled with +``-fsanitize=cfi-vcall`` enabled and be statically linked into the program. Performance ~~~~~~~~~~~ @@ -85,15 +83,13 @@ restriction can normally be enforced. However it may in some cases be necessary for a function to perform a forbidden cast to conform with an external API (e.g. the ``allocate`` member function of a standard library allocator). Such -functions may be blacklisted using a :doc:`SanitizerSpecialCaseList`. +functions may be :ref:`blacklisted `. For this scheme to work, all translation units containing the definition -of a virtual member function (whether inline or not) must be compiled with +of a virtual member function (whether inline or not), other than members +of :ref:`blacklisted ` types, must be compiled with ``-fsanitize=cfi-derived-cast`` or ``-fsanitize=cfi-unrelated-cast`` enabled -and be statically linked into the program. Classes in the C++ standard library -(under namespace ``std``) are exempted from checking, and therefore programs -may be linked against a pre-built standard library, but this may change in -the future. +and be statically linked into the program. Non-Virtual Member Function Call Checking ----------------------------------------- @@ -106,11 +102,9 @@ ``-fsanitize=cfi-nvcall``. For this scheme to work, all translation units containing the definition -of a virtual member function (whether inline or not) must be compiled -with ``-fsanitize=cfi-nvcall`` enabled and be statically linked into the -program. Classes in the C++ standard library (under namespace ``std``) are -exempted from checking, and therefore programs may be linked against a -pre-built standard library, but this may change in the future. +of a virtual member function (whether inline or not), other than members +of :ref:`blacklisted ` types, must be compiled with +``-fsanitize=cfi-nvcall`` enabled and be statically linked into the program. .. _cfi-strictness: @@ -129,6 +123,32 @@ most compilers and should not have security implications, so we allow it by default. It can be disabled with ``-fsanitize=cfi-cast-strict``. +.. _cfi-blacklist: + +Blacklist +--------- + +A :doc:`SanitizerSpecialCaseList` can be used to relax CFI checks for certain +source files, functions and types using the ``src``, ``fun`` and ``type`` +entity types. + +In addition, if a type has a ``uuid`` attribute and the blacklist contains +the type entry ``attr:uuid``, CFI checks are suppressed for that type. This +allows all COM types to be easily blacklisted, which is useful as COM types +are typically defined outside of the linked program. + +.. code-block:: bash + + # Suppress checking for code in a file. + src:bad_file.cpp + src:bad_header.h + # Ignore all functions with names containing MyFooBar. + fun:*MyFooBar* + # Ignore all types in the standard library. + type:std::* + # Ignore all types with a uuid attribute. + type:attr:uuid + Design ------ Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -2168,15 +2168,6 @@ if (!ClassDecl->isCompleteDefinition() || !ClassDecl->isDynamicClass()) return; - SmallString<64> MangledName; - llvm::raw_svector_ostream Out(MangledName); - CGM.getCXXABI().getMangleContext().mangleCXXRTTI(T.getUnqualifiedType(), - Out); - - // Blacklist based on the mangled type. - if (CGM.getContext().getSanitizerBlacklist().isBlacklistedType(Out.str())) - return; - if (!SanOpts.has(SanitizerKind::CFICastStrict)) ClassDecl = LeastDerivedClassWithSameLayout(ClassDecl); Index: lib/CodeGen/CGVTables.cpp =================================================================== --- lib/CodeGen/CGVTables.cpp +++ lib/CodeGen/CGVTables.cpp @@ -842,8 +842,12 @@ } bool CodeGenModule::IsCFIBlacklistedRecord(const CXXRecordDecl *RD) { - // FIXME: Make this user configurable. - return RD->isInStdNamespace(); + if (RD->hasAttr() && + getContext().getSanitizerBlacklist().isBlacklistedType("attr:uuid")) + return true; + + return getContext().getSanitizerBlacklist().isBlacklistedType( + RD->getQualifiedNameAsString()); } void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable, Index: lib/Driver/SanitizerArgs.cpp =================================================================== --- lib/Driver/SanitizerArgs.cpp +++ lib/Driver/SanitizerArgs.cpp @@ -90,6 +90,8 @@ BlacklistFile = "tsan_blacklist.txt"; else if (Kinds & DataFlow) BlacklistFile = "dfsan_abilist.txt"; + else if (Kinds & CFI) + BlacklistFile = "cfi_blacklist.txt"; if (BlacklistFile) { clang::SmallString<64> Path(D.ResourceDir); Index: test/CodeGenCXX/cfi-blacklist.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/cfi-blacklist.cpp @@ -0,0 +1,30 @@ +// RUN: echo "type:attr:uuid" > %t.txt +// RUN: %clang_cc1 -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOUUID %s +// RUN: echo "type:std::*" > %t.txt +// RUN: %clang_cc1 -fms-extensions -fsanitize=cfi-vcall -fsanitize-blacklist=%t.txt -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=NOSTD %s + +struct __declspec(uuid("00000000-0000-0000-0000-000000000000")) S1 { + virtual void f(); +}; + +namespace std { + +struct S2 { + virtual void f(); +}; + +} + +// CHECK: define{{.*}}s1f +// NOSTD: llvm.bitset.test +// NOUUID-NOT: llvm.bitset.test +void s1f(S1 *s1) { + s1->f(); +} + +// CHECK: define{{.*}}s2f +// NOSTD-NOT: llvm.bitset.test +// NOUUID: llvm.bitset.test +void s2f(std::S2 *s2) { + s2->f(); +}