Skip to content

Commit f476803

Browse files
committed
[clang] Fix PointerAuth semantics of cpp_trivially_relocatable
This adds a function to ASTContext to query whether a type contains values with address discriminated pointer auth, and performs the required semantic checks to ensure correct reporting of relocatablity in those cases. For the standardized version, __builtin_is_cpp_trivially_relocatable this means rejecting unions of types containing address discriminated values. For the old deprecated __builtin_is_trivially_relocatable this means rejecting any type containing an address discriminated value. 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 c0c0f60 commit f476803

File tree

6 files changed

+169
-11
lines changed

6 files changed

+169
-11
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,10 +628,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
628628
getRelocationInfoForCXXRecord(const CXXRecordDecl *) const;
629629
void setRelocationInfoForCXXRecord(const CXXRecordDecl *,
630630
CXXRecordDeclRelocationInfo);
631+
bool containsAddressDiscriminatedPointerAuth(QualType T);
631632

632633
private:
633634
llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo>
634635
RelocatableClasses;
636+
llvm::DenseMap<const RecordDecl *, bool>
637+
RecordContainsAddressDiscriminatedPointerAuth;
635638

636639
ImportDecl *FirstLocalImport = nullptr;
637640
ImportDecl *LastLocalImport = nullptr;
@@ -3668,6 +3671,7 @@ OPT_LIST(V)
36683671
/// authentication policy for the specified record.
36693672
const CXXRecordDecl *
36703673
baseForVTableAuthentication(const CXXRecordDecl *ThisClass);
3674+
bool hasAddressDiscriminatedVTableAuthentication(const CXXRecordDecl *Class);
36713675
bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
36723676
StringRef MangledName);
36733677

clang/lib/AST/ASTContext.cpp

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

1708+
bool ASTContext::containsAddressDiscriminatedPointerAuth(QualType T) {
1709+
if (!LangOpts.PointerAuthCalls && !LangOpts.PointerAuthIntrinsics)
1710+
return false;
1711+
1712+
T = T.getCanonicalType();
1713+
if (T.hasAddressDiscriminatedPointerAuth())
1714+
return true;
1715+
const RecordDecl *RD = T->getAsRecordDecl();
1716+
if (!RD)
1717+
return false;
1718+
1719+
auto SaveReturn = [this, RD](bool Result) {
1720+
RecordContainsAddressDiscriminatedPointerAuth.insert({RD, Result});
1721+
return Result;
1722+
};
1723+
if (auto Existing = RecordContainsAddressDiscriminatedPointerAuth.find(RD);
1724+
Existing != RecordContainsAddressDiscriminatedPointerAuth.end())
1725+
return Existing->second;
1726+
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
1727+
if (CXXRD->isPolymorphic() &&
1728+
hasAddressDiscriminatedVTableAuthentication(CXXRD))
1729+
return SaveReturn(true);
1730+
for (auto Base : CXXRD->bases()) {
1731+
if (containsAddressDiscriminatedPointerAuth(Base.getType()))
1732+
return SaveReturn(true);
1733+
}
1734+
}
1735+
for (auto *FieldDecl : RD->fields()) {
1736+
if (containsAddressDiscriminatedPointerAuth(FieldDecl->getType()))
1737+
return SaveReturn(true);
1738+
}
1739+
return SaveReturn(false);
1740+
}
1741+
17081742
void ASTContext::addedLocalImportDecl(ImportDecl *Import) {
17091743
assert(!Import->getNextLocalImport() &&
17101744
"Import declaration already in the chain");
@@ -15121,6 +15155,21 @@ ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) {
1512115155
return PrimaryBase;
1512215156
}
1512315157

15158+
bool ASTContext::hasAddressDiscriminatedVTableAuthentication(
15159+
const CXXRecordDecl *Class) {
15160+
assert(Class->isPolymorphic());
15161+
const CXXRecordDecl *BaseType = baseForVTableAuthentication(Class);
15162+
using AuthAttr = VTablePointerAuthenticationAttr;
15163+
const auto *ExplicitAuth = BaseType->getAttr<AuthAttr>();
15164+
if (!ExplicitAuth)
15165+
return LangOpts.PointerAuthVTPtrAddressDiscrimination;
15166+
AuthAttr::AddressDiscriminationMode AddressDiscrimination =
15167+
ExplicitAuth->getAddressDiscrimination();
15168+
if (AddressDiscrimination == AuthAttr::DefaultAddressDiscrimination)
15169+
return LangOpts.PointerAuthVTPtrAddressDiscrimination;
15170+
return AddressDiscrimination == AuthAttr::AddressDiscrimination;
15171+
}
15172+
1512415173
bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
1512515174
StringRef MangledName) {
1512615175
auto *Method = cast<CXXMethodDecl>(VirtualMethodDecl.getDecl());

clang/lib/Sema/SemaTypeTraits.cpp

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

191+
bool IsUnion = D->isUnion();
191192
for (const FieldDecl *Field : D->fields()) {
192-
if (Field->getType()->isDependentType())
193+
QualType FieldType = Field->getType();
194+
if (FieldType->isDependentType())
193195
continue;
194-
if (Field->getType()->isReferenceType())
196+
if (FieldType->isReferenceType())
195197
continue;
196198
// ... has a non-static data member of an object type that is not
197199
// of a trivially relocatable type
198200
if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
199201
return false;
202+
if (IsUnion &&
203+
SemaRef.Context.containsAddressDiscriminatedPointerAuth(FieldType))
204+
return false;
200205
}
201206
return !D->hasDeletedDestructor();
202207
}
@@ -322,9 +327,6 @@ bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {
322327
if (BaseElementType.hasNonTrivialObjCLifetime())
323328
return false;
324329

325-
if (BaseElementType.hasAddressDiscriminatedPointerAuth())
326-
return false;
327-
328330
if (BaseElementType->isIncompleteType())
329331
return false;
330332

@@ -670,7 +672,7 @@ static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) {
670672
if (!BaseElementType->isObjectType())
671673
return false;
672674

673-
if (T.hasAddressDiscriminatedPointerAuth())
675+
if (SemaRef.getASTContext().containsAddressDiscriminatedPointerAuth(T))
674676
return false;
675677

676678
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: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static_assert(!__is_trivially_assignable(S1, const S1&));
2626
static_assert(__is_trivially_destructible(S1));
2727
static_assert(!__is_trivially_copyable(S1));
2828
static_assert(!__is_trivially_relocatable(S1)); // expected-warning{{deprecated}}
29-
static_assert(!__builtin_is_cpp_trivially_relocatable(S1));
29+
static_assert(__builtin_is_cpp_trivially_relocatable(S1));
3030
static_assert(!__is_trivially_equality_comparable(S1));
3131

3232
static_assert(__is_trivially_constructible(Holder<S1>));
@@ -35,7 +35,7 @@ static_assert(!__is_trivially_assignable(Holder<S1>, const Holder<S1>&));
3535
static_assert(__is_trivially_destructible(Holder<S1>));
3636
static_assert(!__is_trivially_copyable(Holder<S1>));
3737
static_assert(!__is_trivially_relocatable(Holder<S1>)); // expected-warning{{deprecated}}
38-
static_assert(!__builtin_is_cpp_trivially_relocatable(Holder<S1>));
38+
static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S1>));
3939
static_assert(!__is_trivially_equality_comparable(Holder<S1>));
4040

4141
struct S2 {
@@ -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

@@ -148,7 +148,7 @@ static_assert(!__is_trivially_assignable(S6, const S6&));
148148
static_assert(__is_trivially_destructible(S6));
149149
static_assert(!__is_trivially_copyable(S6));
150150
static_assert(!__is_trivially_relocatable(S6)); // expected-warning{{deprecated}}
151-
static_assert(!__builtin_is_cpp_trivially_relocatable(S6));
151+
static_assert(__builtin_is_cpp_trivially_relocatable(S6));
152152
static_assert(!__is_trivially_equality_comparable(S6));
153153

154154
static_assert(__is_trivially_constructible(Holder<S6>));
@@ -157,7 +157,7 @@ static_assert(!__is_trivially_assignable(Holder<S6>, const Holder<S6>&));
157157
static_assert(__is_trivially_destructible(Holder<S6>));
158158
static_assert(!__is_trivially_copyable(Holder<S6>));
159159
static_assert(!__is_trivially_relocatable(Holder<S6>)); // expected-warning{{deprecated}}
160-
static_assert(!__builtin_is_cpp_trivially_relocatable(Holder<S6>));
160+
static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S6>));
161161
static_assert(!__is_trivially_equality_comparable(Holder<S6>));
162162

163163
struct S7 {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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 MultipleMembers {
28+
NonAddressDiscPtrauth field0;
29+
AddressDiscPtrauth field1;
30+
};
31+
32+
static_assert(__builtin_is_cpp_trivially_relocatable(MultipleMembers));
33+
34+
struct UnionOfPtrauth {
35+
union {
36+
NonAddressDiscPtrauth field0;
37+
AddressDiscPtrauth field1;
38+
} u;
39+
};
40+
41+
static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfPtrauth));
42+
43+
struct [[clang::ptrauth_vtable_pointer(process_independent,address_discrimination,no_extra_discrimination)]] Polymorphic trivially_relocatable_if_eligible {
44+
virtual ~Polymorphic();
45+
};
46+
47+
struct Foo : Polymorphic {
48+
Foo(const Foo&);
49+
~Foo();
50+
};
51+
52+
53+
static_assert(__builtin_is_cpp_trivially_relocatable(Polymorphic));
54+
55+
struct [[clang::ptrauth_vtable_pointer(process_independent,no_address_discrimination,no_extra_discrimination)]] NonAddressDiscriminatedPolymorphic trivially_relocatable_if_eligible {
56+
virtual ~NonAddressDiscriminatedPolymorphic();
57+
};
58+
59+
static_assert(__builtin_is_cpp_trivially_relocatable(NonAddressDiscriminatedPolymorphic));
60+
61+
62+
struct PolymorphicMembers {
63+
Polymorphic field;
64+
};
65+
66+
static_assert(__builtin_is_cpp_trivially_relocatable(PolymorphicMembers));
67+
68+
struct UnionOfPolymorphic {
69+
union trivially_relocatable_if_eligible {
70+
Polymorphic p;
71+
int i;
72+
} u;
73+
};
74+
75+
static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfPolymorphic));
76+
77+
78+
struct UnionOfNonAddressDiscriminatedPolymorphic {
79+
union trivially_relocatable_if_eligible {
80+
NonAddressDiscriminatedPolymorphic p;
81+
int i;
82+
} u;
83+
};
84+
static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfNonAddressDiscriminatedPolymorphic));
85+
86+
struct UnionOfNonAddressDiscriminatedPtrauth {
87+
union {
88+
NonAddressDiscPtrauth p;
89+
int i;
90+
} u;
91+
};
92+
93+
static_assert(__builtin_is_cpp_trivially_relocatable(UnionOfNonAddressDiscriminatedPtrauth));
94+
95+
struct UnionOfAddressDisriminatedPtrauth {
96+
union {
97+
AddressDiscPtrauth p;
98+
int i;
99+
} u;
100+
};
101+
102+
static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfAddressDisriminatedPtrauth));

0 commit comments

Comments
 (0)