Skip to content

Commit eddab9b

Browse files
authored
[clang] Fix PointerAuth semantics of cpp_trivially_relocatable (#143969)
This adds a number of functions to ASTContext to query whether a type contains data protected with address discriminated pointer authentication, and whether the protected values are just vtable pointers, or if there are other address discriminated types included. For the standardized version, __builtin_is_cpp_trivially_relocatable this means accepting types where the only address discriminated values are vtable pointers. Other address discriminated types are not considered relocatable. In addition to that any union containing any address discriminated data, including vtable pointers, is not relocatable. For the old deprecated __builtin_is_trivially_relocatable we reject any type containing any address discriminated value, as it is semantically intended as being a "is this memcopyable" which is not true for anything with address discrimination. This PR does not update the codegen for __builtin_trivially_relocate, that will be in a follow on PR that is much more complex.
1 parent 79a2b15 commit eddab9b

File tree

6 files changed

+268
-9
lines changed

6 files changed

+268
-9
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,10 +629,48 @@ class ASTContext : public RefCountedBase<ASTContext> {
629629
void setRelocationInfoForCXXRecord(const CXXRecordDecl *,
630630
CXXRecordDeclRelocationInfo);
631631

632+
/// Examines a given type, and returns whether the type itself
633+
/// is address discriminated, or any transitively embedded types
634+
/// contain data that is address discriminated. This includes
635+
/// implicitly authenticated values like vtable pointers, as well as
636+
/// explicitly qualified fields.
637+
bool containsAddressDiscriminatedPointerAuth(QualType T) {
638+
if (!isPointerAuthenticationAvailable())
639+
return false;
640+
return findPointerAuthContent(T) != PointerAuthContent::None;
641+
}
642+
643+
/// Examines a given type, and returns whether the type itself
644+
/// or any data it transitively contains has a pointer authentication
645+
/// schema that is not safely relocatable. e.g. any data or fields
646+
/// with address discrimination other than any otherwise similar
647+
/// vtable pointers.
648+
bool containsNonRelocatablePointerAuth(QualType T) {
649+
if (!isPointerAuthenticationAvailable())
650+
return false;
651+
return findPointerAuthContent(T) ==
652+
PointerAuthContent::AddressDiscriminatedData;
653+
}
654+
632655
private:
633656
llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo>
634657
RelocatableClasses;
635658

659+
// FIXME: store in RecordDeclBitfields in future?
660+
enum class PointerAuthContent : uint8_t {
661+
None,
662+
AddressDiscriminatedVTable,
663+
AddressDiscriminatedData
664+
};
665+
666+
// A simple helper function to short circuit pointer auth checks.
667+
bool isPointerAuthenticationAvailable() const {
668+
return LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics;
669+
}
670+
PointerAuthContent findPointerAuthContent(QualType T);
671+
llvm::DenseMap<const RecordDecl *, PointerAuthContent>
672+
RecordContainsAddressDiscriminatedPointerAuth;
673+
636674
ImportDecl *FirstLocalImport = nullptr;
637675
ImportDecl *LastLocalImport = nullptr;
638676

@@ -3668,6 +3706,7 @@ OPT_LIST(V)
36683706
/// authentication policy for the specified record.
36693707
const CXXRecordDecl *
36703708
baseForVTableAuthentication(const CXXRecordDecl *ThisClass);
3709+
36713710
bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
36723711
StringRef MangledName);
36733712

clang/lib/AST/ASTContext.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,73 @@ void ASTContext::setRelocationInfoForCXXRecord(
17051705
RelocatableClasses.insert({D, Info});
17061706
}
17071707

1708+
static bool primaryBaseHaseAddressDiscriminatedVTableAuthentication(
1709+
ASTContext &Context, const CXXRecordDecl *Class) {
1710+
if (!Class->isPolymorphic())
1711+
return false;
1712+
const CXXRecordDecl *BaseType = Context.baseForVTableAuthentication(Class);
1713+
using AuthAttr = VTablePointerAuthenticationAttr;
1714+
const AuthAttr *ExplicitAuth = BaseType->getAttr<AuthAttr>();
1715+
if (!ExplicitAuth)
1716+
return Context.getLangOpts().PointerAuthVTPtrAddressDiscrimination;
1717+
AuthAttr::AddressDiscriminationMode AddressDiscrimination =
1718+
ExplicitAuth->getAddressDiscrimination();
1719+
if (AddressDiscrimination == AuthAttr::DefaultAddressDiscrimination)
1720+
return Context.getLangOpts().PointerAuthVTPtrAddressDiscrimination;
1721+
return AddressDiscrimination == AuthAttr::AddressDiscrimination;
1722+
}
1723+
1724+
ASTContext::PointerAuthContent ASTContext::findPointerAuthContent(QualType T) {
1725+
assert(isPointerAuthenticationAvailable());
1726+
1727+
T = T.getCanonicalType();
1728+
if (T.hasAddressDiscriminatedPointerAuth())
1729+
return PointerAuthContent::AddressDiscriminatedData;
1730+
const RecordDecl *RD = T->getAsRecordDecl();
1731+
if (!RD)
1732+
return PointerAuthContent::None;
1733+
1734+
if (auto Existing = RecordContainsAddressDiscriminatedPointerAuth.find(RD);
1735+
Existing != RecordContainsAddressDiscriminatedPointerAuth.end())
1736+
return Existing->second;
1737+
1738+
PointerAuthContent Result = PointerAuthContent::None;
1739+
1740+
auto SaveResultAndReturn = [&]() -> PointerAuthContent {
1741+
auto [ResultIter, DidAdd] =
1742+
RecordContainsAddressDiscriminatedPointerAuth.try_emplace(RD, Result);
1743+
(void)ResultIter;
1744+
(void)DidAdd;
1745+
assert(DidAdd);
1746+
return Result;
1747+
};
1748+
auto ShouldContinueAfterUpdate = [&](PointerAuthContent NewResult) {
1749+
static_assert(PointerAuthContent::None <
1750+
PointerAuthContent::AddressDiscriminatedVTable);
1751+
static_assert(PointerAuthContent::AddressDiscriminatedVTable <
1752+
PointerAuthContent::AddressDiscriminatedData);
1753+
if (NewResult > Result)
1754+
Result = NewResult;
1755+
return Result != PointerAuthContent::AddressDiscriminatedData;
1756+
};
1757+
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
1758+
if (primaryBaseHaseAddressDiscriminatedVTableAuthentication(*this, CXXRD) &&
1759+
!ShouldContinueAfterUpdate(
1760+
PointerAuthContent::AddressDiscriminatedVTable))
1761+
return SaveResultAndReturn();
1762+
for (auto Base : CXXRD->bases()) {
1763+
if (!ShouldContinueAfterUpdate(findPointerAuthContent(Base.getType())))
1764+
return SaveResultAndReturn();
1765+
}
1766+
}
1767+
for (auto *FieldDecl : RD->fields()) {
1768+
if (!ShouldContinueAfterUpdate(
1769+
findPointerAuthContent(FieldDecl->getType())))
1770+
return SaveResultAndReturn();
1771+
}
1772+
return SaveResultAndReturn();
1773+
}
1774+
17081775
void ASTContext::addedLocalImportDecl(ImportDecl *Import) {
17091776
assert(!Import->getNextLocalImport() &&
17101777
"Import declaration already in the chain");

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ static bool IsEligibleForTrivialRelocation(Sema &SemaRef,
188188
return false;
189189
}
190190

191+
bool IsUnion = D->isUnion();
191192
for (const FieldDecl *Field : D->fields()) {
192193
if (Field->getType()->isDependentType())
193194
continue;
@@ -197,6 +198,12 @@ static bool IsEligibleForTrivialRelocation(Sema &SemaRef,
197198
// of a trivially relocatable type
198199
if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
199200
return false;
201+
202+
// A union contains values with address discriminated pointer auth
203+
// cannot be relocated.
204+
if (IsUnion && SemaRef.Context.containsAddressDiscriminatedPointerAuth(
205+
Field->getType()))
206+
return false;
200207
}
201208
return !D->hasDeletedDestructor();
202209
}
@@ -313,7 +320,6 @@ bool Sema::IsCXXTriviallyRelocatableType(const CXXRecordDecl &RD) {
313320
}
314321

315322
bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {
316-
317323
QualType BaseElementType = getASTContext().getBaseElementType(Type);
318324

319325
if (Type->isVariableArrayType())
@@ -322,10 +328,10 @@ bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {
322328
if (BaseElementType.hasNonTrivialObjCLifetime())
323329
return false;
324330

325-
if (BaseElementType.hasAddressDiscriminatedPointerAuth())
331+
if (BaseElementType->isIncompleteType())
326332
return false;
327333

328-
if (BaseElementType->isIncompleteType())
334+
if (Context.containsNonRelocatablePointerAuth(Type))
329335
return false;
330336

331337
if (BaseElementType->isScalarType() || BaseElementType->isVectorType())
@@ -670,7 +676,10 @@ static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) {
670676
if (!BaseElementType->isObjectType())
671677
return false;
672678

673-
if (T.hasAddressDiscriminatedPointerAuth())
679+
// The deprecated __builtin_is_trivially_relocatable does not have
680+
// an equivalent to __builtin_trivially_relocate, so there is no
681+
// safe way to use it if there are any address discriminated values.
682+
if (SemaRef.getASTContext().containsAddressDiscriminatedPointerAuth(T))
674683
return false;
675684

676685
if (const auto *RD = BaseElementType->getAsCXXRecordDecl();

clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -std=c++2c -verify %s
2+
// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-intrinsics -fptrauth-calls -std=c++2c -verify %s
23

34
class Trivial {};
45
static_assert(__builtin_is_cpp_trivially_relocatable(Trivial));

clang/test/SemaCXX/ptrauth-triviality.cpp

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++20 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s
2-
// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++20 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s
1+
// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++26 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s
2+
// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++26 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s
33

44
#define AQ __ptrauth(1,1,50)
55
#define IQ __ptrauth(1,0,50)
@@ -83,7 +83,7 @@ static_assert(!__is_trivially_constructible(Holder<S3>, const Holder<S3>&));
8383
static_assert(!__is_trivially_assignable(Holder<S3>, const Holder<S3>&));
8484
static_assert(__is_trivially_destructible(Holder<S3>));
8585
static_assert(!__is_trivially_copyable(Holder<S3>));
86-
static_assert(__is_trivially_relocatable(Holder<S3>)); // expected-warning{{deprecated}}
86+
static_assert(!__is_trivially_relocatable(Holder<S3>)); // expected-warning{{deprecated}}
8787
static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S3>));
8888
static_assert(!__is_trivially_equality_comparable(Holder<S3>));
8989

@@ -99,7 +99,6 @@ static_assert(!__is_trivially_assignable(S4, const S4&));
9999
static_assert(__is_trivially_destructible(S4));
100100
static_assert(!__is_trivially_copyable(S4));
101101
static_assert(!__is_trivially_relocatable(S4)); // expected-warning{{deprecated}}
102-
//FIXME
103102
static_assert(__builtin_is_cpp_trivially_relocatable(S4));
104103
static_assert(!__is_trivially_equality_comparable(S4));
105104

@@ -124,7 +123,6 @@ static_assert(!__is_trivially_assignable(S5, const S5&));
124123
static_assert(__is_trivially_destructible(S5));
125124
static_assert(!__is_trivially_copyable(S5));
126125
static_assert(!__is_trivially_relocatable(S5)); // expected-warning{{deprecated}}
127-
//FIXME
128126
static_assert(__builtin_is_cpp_trivially_relocatable(S5));
129127
static_assert(!__is_trivially_equality_comparable(S5));
130128

@@ -182,3 +180,39 @@ static_assert(__is_trivially_copyable(Holder<S7>));
182180
static_assert(__is_trivially_relocatable(Holder<S7>)); // expected-warning{{deprecated}}
183181
static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S7>));
184182
static_assert(__is_trivially_equality_comparable(Holder<S7>));
183+
184+
template <class... Bases> struct MultipleInheriter : Bases... {
185+
};
186+
187+
template <class T> static const bool test_is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T);
188+
template <class... Types> static const bool multiple_inheritance_is_relocatable = test_is_trivially_relocatable_v<MultipleInheriter<Types...>>;
189+
template <class... Types> static const bool inheritance_relocatability_matches_bases_v =
190+
(test_is_trivially_relocatable_v<Types> && ...) == multiple_inheritance_is_relocatable<Types...>;
191+
192+
static_assert(multiple_inheritance_is_relocatable<S4, S5> == multiple_inheritance_is_relocatable<S5, S4>);
193+
static_assert(inheritance_relocatability_matches_bases_v<S4, S5>);
194+
static_assert(inheritance_relocatability_matches_bases_v<S5, S4>);
195+
196+
struct AA AddressDiscriminatedPolymorphicBase trivially_relocatable_if_eligible {
197+
virtual void foo();
198+
};
199+
200+
struct IA NoAddressDiscriminatedPolymorphicBase trivially_relocatable_if_eligible {
201+
virtual void bar();
202+
};
203+
204+
template <class T> struct UnionWrapper trivially_relocatable_if_eligible {
205+
union U {
206+
T field1;
207+
} u;
208+
};
209+
210+
static_assert(test_is_trivially_relocatable_v<AddressDiscriminatedPolymorphicBase>);
211+
static_assert(test_is_trivially_relocatable_v<NoAddressDiscriminatedPolymorphicBase>);
212+
static_assert(inheritance_relocatability_matches_bases_v<AddressDiscriminatedPolymorphicBase, NoAddressDiscriminatedPolymorphicBase>);
213+
static_assert(inheritance_relocatability_matches_bases_v<NoAddressDiscriminatedPolymorphicBase, AddressDiscriminatedPolymorphicBase>);
214+
215+
static_assert(!test_is_trivially_relocatable_v<UnionWrapper<AddressDiscriminatedPolymorphicBase>>);
216+
static_assert(test_is_trivially_relocatable_v<UnionWrapper<NoAddressDiscriminatedPolymorphicBase>>);
217+
static_assert(!test_is_trivially_relocatable_v<UnionWrapper<MultipleInheriter<NoAddressDiscriminatedPolymorphicBase, AddressDiscriminatedPolymorphicBase>>>);
218+
static_assert(!test_is_trivially_relocatable_v<UnionWrapper<MultipleInheriter<AddressDiscriminatedPolymorphicBase, NoAddressDiscriminatedPolymorphicBase>>>);
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// RUN: %clang_cc1 -triple arm64 -fptrauth-calls -fptrauth-intrinsics -std=c++26 -verify %s
2+
3+
// This test intentionally does not enable the global address discrimination
4+
// of vtable pointers. This lets us configure them with different schemas
5+
// and verify that we're correctly tracking the existence of address discrimination
6+
7+
// expected-no-diagnostics
8+
9+
struct NonAddressDiscPtrauth {
10+
void * __ptrauth(1, 0, 1234) p;
11+
};
12+
13+
static_assert(__builtin_is_cpp_trivially_relocatable(NonAddressDiscPtrauth));
14+
15+
struct AddressDiscPtrauth {
16+
void * __ptrauth(1, 1, 1234) p;
17+
};
18+
19+
static_assert(!__builtin_is_cpp_trivially_relocatable(AddressDiscPtrauth));
20+
21+
struct MultipleBaseClasses : NonAddressDiscPtrauth, AddressDiscPtrauth {
22+
23+
};
24+
25+
static_assert(!__builtin_is_cpp_trivially_relocatable(MultipleBaseClasses));
26+
27+
struct MultipleMembers1 {
28+
NonAddressDiscPtrauth field0;
29+
AddressDiscPtrauth field1;
30+
};
31+
32+
static_assert(!__builtin_is_cpp_trivially_relocatable(MultipleMembers1));
33+
34+
struct MultipleMembers2 {
35+
NonAddressDiscPtrauth field0;
36+
NonAddressDiscPtrauth field1;
37+
};
38+
39+
static_assert(__builtin_is_cpp_trivially_relocatable(MultipleMembers2));
40+
41+
struct UnionOfPtrauth {
42+
union {
43+
NonAddressDiscPtrauth field0;
44+
AddressDiscPtrauth field1;
45+
} u;
46+
};
47+
48+
static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfPtrauth));
49+
50+
struct [[clang::ptrauth_vtable_pointer(process_independent,address_discrimination,no_extra_discrimination)]] Polymorphic trivially_relocatable_if_eligible {
51+
virtual ~Polymorphic();
52+
};
53+
54+
struct Foo : Polymorphic {
55+
Foo(const Foo&);
56+
~Foo();
57+
};
58+
59+
60+
static_assert(__builtin_is_cpp_trivially_relocatable(Polymorphic));
61+
62+
struct [[clang::ptrauth_vtable_pointer(process_independent,no_address_discrimination,no_extra_discrimination)]] NonAddressDiscriminatedPolymorphic trivially_relocatable_if_eligible {
63+
virtual ~NonAddressDiscriminatedPolymorphic();
64+
};
65+
66+
static_assert(__builtin_is_cpp_trivially_relocatable(NonAddressDiscriminatedPolymorphic));
67+
68+
69+
struct PolymorphicMembers {
70+
Polymorphic field;
71+
};
72+
73+
static_assert(__builtin_is_cpp_trivially_relocatable(PolymorphicMembers));
74+
75+
struct UnionOfPolymorphic {
76+
union trivially_relocatable_if_eligible {
77+
Polymorphic p;
78+
int i;
79+
} u;
80+
};
81+
82+
static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfPolymorphic));
83+
84+
85+
struct UnionOfNonAddressDiscriminatedPolymorphic {
86+
union trivially_relocatable_if_eligible {
87+
NonAddressDiscriminatedPolymorphic p;
88+
int i;
89+
} u;
90+
};
91+
static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfNonAddressDiscriminatedPolymorphic));
92+
93+
struct UnionOfNonAddressDiscriminatedPtrauth {
94+
union {
95+
NonAddressDiscPtrauth p;
96+
int i;
97+
} u;
98+
};
99+
100+
static_assert(__builtin_is_cpp_trivially_relocatable(UnionOfNonAddressDiscriminatedPtrauth));
101+
102+
struct UnionOfAddressDisriminatedPtrauth {
103+
union {
104+
AddressDiscPtrauth p;
105+
int i;
106+
} u;
107+
};
108+
109+
static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfAddressDisriminatedPtrauth));

0 commit comments

Comments
 (0)