CodeGen: Improve CFI type blacklisting mechanism.

We now use the sanitizer special case list to decide which types to blacklist.
We also support a special blacklist entry for types with a uuid attribute,
which are generally COM types whose virtual tables are defined externally.

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

llvm-svn: 242286
This commit is contained in:
Peter Collingbourne 2015-07-15 12:15:56 +00:00
parent 3bed68cfc7
commit 6fccf95aad
5 changed files with 74 additions and 27 deletions

View File

@ -41,11 +41,9 @@ derived class of the static type of the object used to make the call.
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 <cfi-blacklist>` types, must be compiled with
``-fsanitize=cfi-vcall`` enabled and be statically linked into the program.
Performance
~~~~~~~~~~~
@ -85,15 +83,13 @@ If a program as a matter of policy forbids the second type of cast, that
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 <cfi-blacklist>`.
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 <cfi-blacklist>` 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 @@ polymorphic class type. This CFI scheme can be enabled on its own using
``-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 <cfi-blacklist>` types, must be compiled with
``-fsanitize=cfi-nvcall`` enabled and be statically linked into the program.
.. _cfi-strictness:
@ -129,6 +123,32 @@ 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``.
.. _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
------

View File

@ -2190,15 +2190,6 @@ void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T,
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);

View File

@ -841,8 +841,12 @@ void CodeGenModule::EmitDeferredVTables() {
}
bool CodeGenModule::IsCFIBlacklistedRecord(const CXXRecordDecl *RD) {
// FIXME: Make this user configurable.
return RD->isInStdNamespace();
if (RD->hasAttr<UuidAttr>() &&
getContext().getSanitizerBlacklist().isBlacklistedType("attr:uuid"))
return true;
return getContext().getSanitizerBlacklist().isBlacklistedType(
RD->getQualifiedNameAsString());
}
void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,

View File

@ -90,6 +90,8 @@ static bool getDefaultBlacklist(const Driver &D, SanitizerMask Kinds,
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);

View File

@ -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();
}