Skip to content

Commit 5b0d0a6

Browse files
committed
Re-apply r267784, r267824 and r267830.
I have updated the compiler-rt tests. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267903 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 3d23e02 commit 5b0d0a6

35 files changed

+435
-209
lines changed

docs/ControlFlowIntegrity.rst

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,25 @@ As currently implemented, all schemes rely on link-time optimization (LTO);
2525
so it is required to specify ``-flto``, and the linker used must support LTO,
2626
for example via the `gold plugin`_.
2727

28-
To allow the checks to be implemented efficiently, the program must be
29-
structured such that certain object files are compiled with CFI
28+
To allow the checks to be implemented efficiently, the program must
29+
be structured such that certain object files are compiled with CFI
3030
enabled, and are statically linked into the program. This may preclude
31-
the use of shared libraries in some cases. Experimental support for
32-
:ref:`cross-DSO control flow integrity <cfi-cross-dso>` exists that
33-
does not have these requirements. This cross-DSO support has unstable
34-
ABI at this time.
31+
the use of shared libraries in some cases.
32+
33+
The compiler will only produce CFI checks for a class if it can infer hidden
34+
LTO visibility for that class. LTO visibility is a property of a class that
35+
is inferred from flags and attributes. For more details, see the documentation
36+
for :doc:`LTO visibility <LTOVisibility>`.
37+
38+
The ``-fsanitize=cfi-{vcall,nvcall,derived-cast,unrelated-cast}`` flags
39+
require that a ``-fvisibility=`` flag also be specified. This is because the
40+
default visibility setting is ``-fvisibility=default``, which would disable
41+
CFI checks for classes without visibility attributes. Most users will want
42+
to specify ``-fvisibility=hidden``, which enables CFI checks for such classes.
43+
44+
Experimental support for :ref:`cross-DSO control flow integrity
45+
<cfi-cross-dso>` exists that does not require classes to have hidden LTO
46+
visibility. This cross-DSO support has unstable ABI at this time.
3547

3648
.. _gold plugin: http://llvm.org/docs/GoldPlugin.html
3749

@@ -233,11 +245,6 @@ A :doc:`SanitizerSpecialCaseList` can be used to relax CFI checks for certain
233245
source files, functions and types using the ``src``, ``fun`` and ``type``
234246
entity types.
235247

236-
In addition, if a type has a ``uuid`` attribute and the blacklist contains
237-
the type entry ``attr:uuid``, CFI checks are suppressed for that type. This
238-
allows all COM types to be easily blacklisted, which is useful as COM types
239-
are typically defined outside of the linked program.
240-
241248
.. code-block:: bash
242249
243250
# Suppress checking for code in a file.
@@ -247,8 +254,6 @@ are typically defined outside of the linked program.
247254
fun:*MyFooBar*
248255
# Ignore all types in the standard library.
249256
type:std::*
250-
# Ignore all types with a uuid attribute.
251-
type:attr:uuid
252257
253258
.. _cfi-cross-dso:
254259

@@ -260,6 +265,11 @@ flow integrity mode, which allows all CFI schemes listed above to
260265
apply across DSO boundaries. As in the regular CFI, each DSO must be
261266
built with ``-flto``.
262267

268+
Normally, CFI checks will only be performed for classes that have hidden LTO
269+
visibility. With this flag enabled, the compiler will emit cross-DSO CFI
270+
checks for all classes, except for those which appear in the CFI blacklist
271+
or which use a ``no_sanitize`` attribute.
272+
263273
Design
264274
======
265275

docs/LTOVisibility.rst

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
==============
2+
LTO Visibility
3+
==============
4+
5+
*LTO visibility* is a property of an entity that specifies whether it can be
6+
referenced from outside the current LTO unit. A *linkage unit* is a set of
7+
translation units linked together into an executable or DSO, and a linkage
8+
unit's *LTO unit* is the subset of the linkage unit that is linked together
9+
using link-time optimization; in the case where LTO is not being used, the
10+
linkage unit's LTO unit is empty. Each linkage unit has only a single LTO unit.
11+
12+
The LTO visibility of a class is used by the compiler to determine which
13+
classes the virtual function call optimization and control flow integrity
14+
features apply to. These features use whole-program information, so they
15+
require the entire class hierarchy to be visible in order to work correctly.
16+
17+
If any translation unit in the program uses either of the virtual function
18+
call optimization or control flow integrity features, it is effectively an
19+
ODR violation to define a class with hidden LTO visibility in multiple linkage
20+
units. A class with public LTO visibility may be defined in multiple linkage
21+
units, but the tradeoff is that the virtual function call optimization and
22+
control flow integrity features can only be applied to classes with hidden LTO
23+
visibility. A class's LTO visibility is treated as an ODR-relevant property
24+
of its definition, so it must be consistent between translation units.
25+
26+
In translation units built with LTO, LTO visibility is based on symbol
27+
visibility or, on the Windows platform, the dllimport and dllexport
28+
attributes. When targeting non-Windows platforms, classes with a visibility
29+
other than hidden visibility receive public LTO visibility. When targeting
30+
Windows, classes with dllimport or dllexport attributes receive public LTO
31+
visibility. All other classes receive hidden LTO visibility. Classes with
32+
internal linkage (e.g. classes declared in unnamed namespaces) also receive
33+
hidden LTO visibility.
34+
35+
A class defined in a translation unit built without LTO receives public
36+
LTO visibility regardless of its object file visibility, linkage or other
37+
attributes.
38+
39+
This mechanism will produce the correct result in most cases, but there are
40+
two cases where it may wrongly infer hidden LTO visibility.
41+
42+
1. As a corollary of the above rules, if a linkage unit is produced from a
43+
combination of LTO object files and non-LTO object files, any hidden
44+
visibility class defined in both a translation unit built with LTO and
45+
a translation unit built without LTO must be defined with public LTO
46+
visibility in order to avoid an ODR violation.
47+
48+
2. Some ABIs provide the ability to define an abstract base class without
49+
visibility attributes in multiple linkage units and have virtual calls
50+
to derived classes in other linkage units work correctly. One example of
51+
this is COM on Windows platforms. If the ABI allows this, any base class
52+
used in this way must be defined with public LTO visibility.
53+
54+
Classes that fall into either of these categories can be marked up with the
55+
``[[clang::lto_visibility_public]]`` attribute. To specifically handle the
56+
COM case, classes with the ``__declspec(uuid())`` attribute receive public
57+
LTO visibility. On Windows platforms, clang-cl's ``/MT`` and ``/MTd``
58+
flags statically link the program against a prebuilt standard library;
59+
these flags imply public LTO visibility for every class declared in the
60+
``std`` and ``stdext`` namespaces.
61+
62+
Example
63+
=======
64+
65+
The following example shows how LTO visibility works in practice in several
66+
cases involving two linkage units, ``main`` and ``dso.so``.
67+
68+
.. code-block:: none
69+
70+
+-----------------------------------------------------------+ +----------------------------------------------------+
71+
| main (clang++ -fvisibility=hidden): | | dso.so (clang++ -fvisibility=hidden): |
72+
| | | |
73+
| +-----------------------------------------------------+ | | struct __attribute__((visibility("default"))) C { |
74+
| | LTO unit (clang++ -fvisibility=hidden -flto): | | | virtual void f(); |
75+
| | | | | } |
76+
| | struct A { ... }; | | | void C::f() {} |
77+
| | struct [[clang::lto_visibility_public]] B { ... }; | | | struct D { |
78+
| | struct __attribute__((visibility("default"))) C { | | | virtual void g() = 0; |
79+
| | virtual void f(); | | | }; |
80+
| | }; | | | struct E : D { |
81+
| | struct [[clang::lto_visibility_public]] D { | | | virtual void g() { ... } |
82+
| | virtual void g() = 0; | | | }; |
83+
| | }; | | | __attribute__(visibility("default"))) D *mkE() { |
84+
| | | | | return new E; |
85+
| +-----------------------------------------------------+ | | } |
86+
| | | |
87+
| struct B { ... }; | +----------------------------------------------------+
88+
| |
89+
+-----------------------------------------------------------+
90+
91+
We will now describe the LTO visibility of each of the classes defined in
92+
these linkage units.
93+
94+
Class ``A`` is not defined outside of ``main``'s LTO unit, so it can have
95+
hidden LTO visibility. This is inferred from the object file visibility
96+
specified on the command line.
97+
98+
Class ``B`` is defined in ``main``, both inside and outside its LTO unit. The
99+
definition outside the LTO unit has public LTO visibility, so the definition
100+
inside the LTO unit must also have public LTO visibility in order to avoid
101+
an ODR violation.
102+
103+
Class ``C`` is defined in both ``main`` and ``dso.so`` and therefore must
104+
have public LTO visibility. This is correctly inferred from the ``visibility``
105+
attribute.
106+
107+
Class ``D`` is an abstract base class with a derived class ``E`` defined
108+
in ``dso.so``. This is an example of the COM scenario; the definition of
109+
``D`` in ``main``'s LTO unit must have public LTO visibility in order to be
110+
compatible with the definition of ``D`` in ``dso.so``, which is observable
111+
by calling the function ``mkE``.

docs/UsersManual.rst

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,17 +1056,8 @@ are listed below.
10561056
.. option:: -fwhole-program-vtables
10571057

10581058
Enable whole-program vtable optimizations, such as single-implementation
1059-
devirtualization and virtual constant propagation. Requires ``-flto``.
1060-
1061-
By default, the compiler will assume that all type hierarchies are
1062-
closed except those in the ``std`` namespace, the ``stdext`` namespace
1063-
and classes with the ``__declspec(uuid())`` attribute.
1064-
1065-
.. option:: -fwhole-program-vtables-blacklist=path
1066-
1067-
Allows the user to specify the path to a list of additional classes to
1068-
blacklist from whole-program vtable optimizations. This list is in the
1069-
:ref:`CFI blacklist <cfi-blacklist>` format.
1059+
devirtualization and virtual constant propagation, for classes with
1060+
:doc:`hidden LTO visibility <LTOVisibility>`. Requires ``-flto``.
10701061

10711062
.. option:: -fno-assume-sane-operator-new
10721063

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Using Clang as a Compiler
3131
SanitizerStats
3232
SanitizerSpecialCaseList
3333
ControlFlowIntegrity
34+
LTOVisibility
3435
SafeStack
3536
Modules
3637
MSVCCompatibility

include/clang/Basic/Attr.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,6 +1611,12 @@ def WeakRef : InheritableAttr {
16111611
let Documentation = [Undocumented];
16121612
}
16131613

1614+
def LTOVisibilityPublic : InheritableAttr {
1615+
let Spellings = [CXX11<"clang", "lto_visibility_public">];
1616+
let Subjects = SubjectList<[Record]>;
1617+
let Documentation = [LTOVisibilityDocs];
1618+
}
1619+
16141620
def AnyX86Interrupt : InheritableAttr, TargetSpecificAttr<TargetAnyX86> {
16151621
// NOTE: If you add any additional spellings, ARMInterrupt's,
16161622
// MSP430Interrupt's and MipsInterrupt's spellings must match.

include/clang/Basic/AttrDocs.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,3 +2380,10 @@ The ``ifunc`` attribute may only be used on a function declaration. A function
23802380
Not all targets support this attribute. ELF targets support this attribute when using binutils v2.20.1 or higher and glibc v2.11.1 or higher. Non-ELF targets currently do not support this attribute.
23812381
}];
23822382
}
2383+
2384+
def LTOVisibilityDocs : Documentation {
2385+
let Category = DocCatType;
2386+
let Content = [{
2387+
See :doc:`LTOVisibility`.
2388+
}];
2389+
}

include/clang/Driver/CC1Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ def fprofile_instrument_path_EQ : Joined<["-"], "fprofile-instrument-path=">,
282282
def fprofile_instrument_use_path_EQ :
283283
Joined<["-"], "fprofile-instrument-use-path=">,
284284
HelpText<"Specify the profile path in PGO use compilation">;
285+
def flto_visibility_public_std:
286+
Flag<["-"], "flto-visibility-public-std">,
287+
HelpText<"Use public LTO visibility for classes in std and stdext namespaces">;
285288

286289
//===----------------------------------------------------------------------===//
287290
// Dependency Output Options

include/clang/Driver/Options.td

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,9 +1152,6 @@ def fwhole_program_vtables : Flag<["-"], "fwhole-program-vtables">, Group<f_Grou
11521152
Flags<[CC1Option]>,
11531153
HelpText<"Enables whole-program vtable optimization. Requires -flto">;
11541154
def fno_whole_program_vtables : Flag<["-"], "fno-whole-program-vtables">, Group<f_Group>;
1155-
def fwhole_program_vtables_blacklist_EQ : Joined<["-"], "fwhole-program-vtables-blacklist=">,
1156-
Group<f_Group>, Flags<[CC1Option]>,
1157-
HelpText<"Path to a blacklist file for whole-program vtable optimization">;
11581155
def fwrapv : Flag<["-"], "fwrapv">, Group<f_Group>, Flags<[CC1Option]>,
11591156
HelpText<"Treat signed integer overflow as two's complement">;
11601157
def fwritable_strings : Flag<["-"], "fwritable-strings">, Group<f_Group>, Flags<[CC1Option]>,

include/clang/Frontend/CodeGenOptions.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ CODEGENOPT(EmitLLVMUseLists, 1, 0) ///< Control whether to serialize use-lists.
187187
CODEGENOPT(WholeProgramVTables, 1, 0) ///< Whether to apply whole-program
188188
/// vtable optimization.
189189

190+
/// Whether to use public LTO visibility for entities in std and stdext
191+
/// namespaces. This is enabled by clang-cl's /MT and /MTd flags.
192+
CODEGENOPT(LTOVisibilityPublicStd, 1, 0)
193+
190194
/// The user specified number of registers to be used for integral arguments,
191195
/// or 0 if unspecified.
192196
VALUE_CODEGENOPT(NumRegisterParameters, 32, 0)

include/clang/Frontend/CodeGenOptions.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,6 @@ class CodeGenOptions : public CodeGenOptionsBase {
199199
/// \brief A list of all -fno-builtin-* function names (e.g., memset).
200200
std::vector<std::string> NoBuiltinFuncs;
201201

202-
/// List of blacklist files for the whole-program vtable optimization feature.
203-
std::vector<std::string> WholeProgramVTablesBlacklistFiles;
204-
205202
public:
206203
// Define accessors/mutators for code generation options of enumeration type.
207204
#define CODEGENOPT(Name, Bits, Default)

lib/CodeGen/CGClass.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2489,7 +2489,7 @@ void CodeGenFunction::EmitBitSetCodeForVCall(const CXXRecordDecl *RD,
24892489
llvm::Value *VTable,
24902490
SourceLocation Loc) {
24912491
if (CGM.getCodeGenOpts().WholeProgramVTables &&
2492-
!CGM.IsBitSetBlacklistedRecord(RD)) {
2492+
CGM.HasHiddenLTOVisibility(RD)) {
24932493
llvm::Metadata *MD =
24942494
CGM.CreateMetadataIdentifierForType(QualType(RD->getTypeForDecl(), 0));
24952495
llvm::Value *BitSetName =
@@ -2565,7 +2565,12 @@ void CodeGenFunction::EmitVTablePtrCheck(const CXXRecordDecl *RD,
25652565
llvm::Value *VTable,
25662566
CFITypeCheckKind TCK,
25672567
SourceLocation Loc) {
2568-
if (CGM.IsBitSetBlacklistedRecord(RD))
2568+
if (!CGM.getCodeGenOpts().SanitizeCfiCrossDso &&
2569+
!CGM.HasHiddenLTOVisibility(RD))
2570+
return;
2571+
2572+
std::string TypeName = RD->getQualifiedNameAsString();
2573+
if (getContext().getSanitizerBlacklist().isBlacklistedType(TypeName))
25692574
return;
25702575

25712576
SanitizerScope SanScope(this);

lib/CodeGen/CGVTables.cpp

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -902,34 +902,43 @@ void CodeGenModule::EmitDeferredVTables() {
902902
DeferredVTables.clear();
903903
}
904904

905-
bool CodeGenModule::NeedVTableBitSets() {
906-
return getCodeGenOpts().WholeProgramVTables ||
907-
getLangOpts().Sanitize.has(SanitizerKind::CFIVCall) ||
908-
getLangOpts().Sanitize.has(SanitizerKind::CFINVCall) ||
909-
getLangOpts().Sanitize.has(SanitizerKind::CFIDerivedCast) ||
910-
getLangOpts().Sanitize.has(SanitizerKind::CFIUnrelatedCast);
911-
}
905+
bool CodeGenModule::HasHiddenLTOVisibility(const CXXRecordDecl *RD) {
906+
LinkageInfo LV = RD->getLinkageAndVisibility();
907+
if (!isExternallyVisible(LV.getLinkage()))
908+
return true;
912909

913-
bool CodeGenModule::IsBitSetBlacklistedRecord(const CXXRecordDecl *RD) {
914-
std::string TypeName = RD->getQualifiedNameAsString();
915-
auto isInBlacklist = [&](const SanitizerBlacklist &BL) {
916-
if (RD->hasAttr<UuidAttr>() && BL.isBlacklistedType("attr:uuid"))
917-
return true;
910+
if (RD->hasAttr<LTOVisibilityPublicAttr>() || RD->hasAttr<UuidAttr>())
911+
return false;
918912

919-
return BL.isBlacklistedType(TypeName);
920-
};
913+
if (getTriple().isOSBinFormatCOFF()) {
914+
if (RD->hasAttr<DLLExportAttr>() || RD->hasAttr<DLLImportAttr>())
915+
return false;
916+
} else {
917+
if (LV.getVisibility() != HiddenVisibility)
918+
return false;
919+
}
921920

922-
return isInBlacklist(WholeProgramVTablesBlacklist) ||
923-
((LangOpts.Sanitize.has(SanitizerKind::CFIVCall) ||
924-
LangOpts.Sanitize.has(SanitizerKind::CFINVCall) ||
925-
LangOpts.Sanitize.has(SanitizerKind::CFIDerivedCast) ||
926-
LangOpts.Sanitize.has(SanitizerKind::CFIUnrelatedCast)) &&
927-
isInBlacklist(getContext().getSanitizerBlacklist()));
921+
if (getCodeGenOpts().LTOVisibilityPublicStd) {
922+
const DeclContext *DC = RD;
923+
while (1) {
924+
auto *D = cast<Decl>(DC);
925+
DC = DC->getParent();
926+
if (isa<TranslationUnitDecl>(DC->getRedeclContext())) {
927+
if (auto *ND = dyn_cast<NamespaceDecl>(D))
928+
if (const IdentifierInfo *II = ND->getIdentifier())
929+
if (II->isStr("std") || II->isStr("stdext"))
930+
return false;
931+
break;
932+
}
933+
}
934+
}
935+
936+
return true;
928937
}
929938

930939
void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
931940
const VTableLayout &VTLayout) {
932-
if (!NeedVTableBitSets())
941+
if (!getCodeGenOpts().PrepareForLTO)
933942
return;
934943

935944
CharUnits PointerWidth =
@@ -938,12 +947,8 @@ void CodeGenModule::EmitVTableBitSetEntries(llvm::GlobalVariable *VTable,
938947
typedef std::pair<const CXXRecordDecl *, unsigned> BSEntry;
939948
std::vector<BSEntry> BitsetEntries;
940949
// Create a bit set entry for each address point.
941-
for (auto &&AP : VTLayout.getAddressPoints()) {
942-
if (IsBitSetBlacklistedRecord(AP.first.getBase()))
943-
continue;
944-
950+
for (auto &&AP : VTLayout.getAddressPoints())
945951
BitsetEntries.push_back(std::make_pair(AP.first.getBase(), AP.second));
946-
}
947952

948953
// Sort the bit set entries for determinism.
949954
std::sort(BitsetEntries.begin(), BitsetEntries.end(),

lib/CodeGen/CodeGenModule.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ CodeGenModule::CodeGenModule(ASTContext &C, const HeaderSearchOptions &HSO,
8888
PreprocessorOpts(PPO), CodeGenOpts(CGO), TheModule(M), Diags(diags),
8989
Target(C.getTargetInfo()), ABI(createCXXABI(*this)),
9090
VMContext(M.getContext()), Types(*this), VTables(*this),
91-
SanitizerMD(new SanitizerMetadata(*this)),
92-
WholeProgramVTablesBlacklist(CGO.WholeProgramVTablesBlacklistFiles,
93-
C.getSourceManager()) {
91+
SanitizerMD(new SanitizerMetadata(*this)) {
9492

9593
// Initialize the type cache.
9694
llvm::LLVMContext &LLVMContext = M.getContext();

0 commit comments

Comments
 (0)