Skip to content

Commit 6b5de16

Browse files
committed
Address discriminated __ptrauth should not be relocatable
After consideration, while "supportable" by relocation, it seems much more reasonable for people to understand that explicitly qualified types can have different relocatability semantics. Implicitly address discriminated fields should be relocatable as developers in general cannot control those behaviours, hence the trivial_relocation language explicitly acknowledges that this impacts unions. Explicitly address discriminated fields do however drastically change the developer aware model of a type, and the practical requirements for efficiently handling __ptrauth qualified types mean that you would want some_addr_discriminated_t[100]; to not be reported as trivially relocatable, while struct { ... some_addr_discriminated_t field; ... } to be reported as such, and the actual decision would regress to what proportion of the object being relocated is subject to such a policy. Having is_trivially_relocatable change behaviour according to some hypothetical internal heuristic is clearly a terrible idea, and so I think for now we should instead simply limit the application of trivial relocation to address discriminated data to those fields that outside of the control of developers.
1 parent 3ba1e39 commit 6b5de16

File tree

5 files changed

+70
-31
lines changed

5 files changed

+70
-31
lines changed

clang/include/clang/AST/ASTContext.h

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

632-
/// Examines a given type, and returns whether the T itself
632+
/// Examines a given type, and returns whether the type itself
633633
/// is address discriminated, or any transitively embedded types
634634
/// contain data that is address discriminated. This includes
635635
/// implicitly authenticated values like vtable pointers, as well as
636636
/// explicitly qualified fields.
637-
bool containsAddressDiscriminatedPointerAuth(QualType T);
638-
// A simple helper function to short circuit pointer auth checks.
637+
bool containsAddressDiscriminatedPointerAuth(QualType T) {
638+
if (!isPointerAuthenticationAvailable())
639+
return false;
640+
return findPointerAuthContent(T) != PointerAuthContent::None;
641+
}
639642

640-
bool isPointerAuthenticationAvailable() const {
641-
return LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics ||
642-
LangOpts.PointerAuthVTPtrAddressDiscrimination;
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) == PointerAuthContent::AddressDiscriminatedData;
643652
}
644653

645654
private:
646655
llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo>
647656
RelocatableClasses;
648657

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

653674
ImportDecl *FirstLocalImport = nullptr;

clang/lib/AST/ASTContext.cpp

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1704,10 +1704,11 @@ void ASTContext::setRelocationInfoForCXXRecord(
17041704
assert(RelocatableClasses.find(D) == RelocatableClasses.end());
17051705
RelocatableClasses.insert({D, Info});
17061706
}
1707+
17071708
static bool
1708-
hasAddressDiscriminatedVTableAuthentication(ASTContext &Context,
1709+
primaryBaseHaseAddressDiscriminatedVTableAuthentication(ASTContext &Context,
17091710
const CXXRecordDecl *Class) {
1710-
if (!Context.isPointerAuthenticationAvailable() || !Class->isPolymorphic())
1711+
if (!Class->isPolymorphic())
17111712
return false;
17121713
const CXXRecordDecl *BaseType = Context.baseForVTableAuthentication(Class);
17131714
using AuthAttr = VTablePointerAuthenticationAttr;
@@ -1721,43 +1722,50 @@ hasAddressDiscriminatedVTableAuthentication(ASTContext &Context,
17211722
return AddressDiscrimination == AuthAttr::AddressDiscrimination;
17221723
}
17231724

1724-
bool ASTContext::containsAddressDiscriminatedPointerAuth(QualType T) {
1725-
if (!isPointerAuthenticationAvailable())
1726-
return false;
1725+
ASTContext::PointerAuthContent ASTContext::findPointerAuthContent(QualType T) {
1726+
assert(isPointerAuthenticationAvailable());
17271727

17281728
T = T.getCanonicalType();
17291729
if (T.hasAddressDiscriminatedPointerAuth())
1730-
return true;
1730+
return PointerAuthContent::AddressDiscriminatedData;
17311731
const RecordDecl *RD = T->getAsRecordDecl();
17321732
if (!RD)
1733-
return false;
1733+
return PointerAuthContent::None;
17341734

17351735
if (auto Existing = RecordContainsAddressDiscriminatedPointerAuth.find(RD);
17361736
Existing != RecordContainsAddressDiscriminatedPointerAuth.end())
17371737
return Existing->second;
17381738

1739-
auto SaveReturn = [this, RD](bool Result) {
1739+
PointerAuthContent Result = PointerAuthContent::None;
1740+
1741+
auto SaveResultAndReturn = [&]() -> PointerAuthContent {
17401742
auto [ResultIter, DidAdd] =
17411743
RecordContainsAddressDiscriminatedPointerAuth.try_emplace(RD, Result);
17421744
(void)ResultIter;
17431745
(void)DidAdd;
17441746
assert(DidAdd);
17451747
return Result;
17461748
};
1749+
auto ShouldContinueAfterUpdate = [&](PointerAuthContent NewResult) {
1750+
static_assert(PointerAuthContent::None < PointerAuthContent::AddressDiscriminatedVTable);
1751+
static_assert(PointerAuthContent::AddressDiscriminatedVTable < PointerAuthContent::AddressDiscriminatedData);
1752+
if (NewResult > Result)
1753+
Result = NewResult;
1754+
return Result != PointerAuthContent::AddressDiscriminatedData;
1755+
};
17471756
if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
1748-
if (CXXRD->isPolymorphic() &&
1749-
hasAddressDiscriminatedVTableAuthentication(*this, CXXRD))
1750-
return SaveReturn(true);
1757+
if (primaryBaseHaseAddressDiscriminatedVTableAuthentication(*this, CXXRD) && !ShouldContinueAfterUpdate(PointerAuthContent::AddressDiscriminatedVTable))
1758+
return SaveResultAndReturn();
17511759
for (auto Base : CXXRD->bases()) {
1752-
if (containsAddressDiscriminatedPointerAuth(Base.getType()))
1753-
return SaveReturn(true);
1760+
if (!ShouldContinueAfterUpdate(findPointerAuthContent(Base.getType())))
1761+
return SaveResultAndReturn();
17541762
}
17551763
}
17561764
for (auto *FieldDecl : RD->fields()) {
1757-
if (containsAddressDiscriminatedPointerAuth(FieldDecl->getType()))
1758-
return SaveReturn(true);
1765+
if (!ShouldContinueAfterUpdate(findPointerAuthContent(FieldDecl->getType())))
1766+
return SaveResultAndReturn();
17591767
}
1760-
return SaveReturn(false);
1768+
return SaveResultAndReturn();
17611769
}
17621770

17631771
void ASTContext::addedLocalImportDecl(ImportDecl *Import) {

clang/lib/Sema/SemaTypeTraits.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {
331331
if (BaseElementType->isIncompleteType())
332332
return false;
333333

334+
if (Context.containsNonRelocatablePointerAuth(Type))
335+
return false;
336+
334337
if (BaseElementType->isScalarType() || BaseElementType->isVectorType())
335338
return true;
336339

clang/test/SemaCXX/ptrauth-triviality.cpp

Lines changed: 4 additions & 4 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 {
@@ -146,7 +146,7 @@ static_assert(!__is_trivially_assignable(S6, const S6&));
146146
static_assert(__is_trivially_destructible(S6));
147147
static_assert(!__is_trivially_copyable(S6));
148148
static_assert(!__is_trivially_relocatable(S6)); // expected-warning{{deprecated}}
149-
static_assert(__builtin_is_cpp_trivially_relocatable(S6));
149+
static_assert(!__builtin_is_cpp_trivially_relocatable(S6));
150150
static_assert(!__is_trivially_equality_comparable(S6));
151151

152152
static_assert(__is_trivially_constructible(Holder<S6>));
@@ -155,7 +155,7 @@ static_assert(!__is_trivially_assignable(Holder<S6>, const Holder<S6>&));
155155
static_assert(__is_trivially_destructible(Holder<S6>));
156156
static_assert(!__is_trivially_copyable(Holder<S6>));
157157
static_assert(!__is_trivially_relocatable(Holder<S6>)); // expected-warning{{deprecated}}
158-
static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S6>));
158+
static_assert(!__builtin_is_cpp_trivially_relocatable(Holder<S6>));
159159
static_assert(!__is_trivially_equality_comparable(Holder<S6>));
160160

161161
struct S7 {

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,27 @@ struct AddressDiscPtrauth {
1616
void * __ptrauth(1, 1, 1234) p;
1717
};
1818

19-
static_assert(__builtin_is_cpp_trivially_relocatable(AddressDiscPtrauth));
19+
static_assert(!__builtin_is_cpp_trivially_relocatable(AddressDiscPtrauth));
2020

2121
struct MultipleBaseClasses : NonAddressDiscPtrauth, AddressDiscPtrauth {
2222

2323
};
2424

25-
static_assert(__builtin_is_cpp_trivially_relocatable(MultipleBaseClasses));
25+
static_assert(!__builtin_is_cpp_trivially_relocatable(MultipleBaseClasses));
2626

27-
struct MultipleMembers {
27+
struct MultipleMembers1 {
2828
NonAddressDiscPtrauth field0;
2929
AddressDiscPtrauth field1;
3030
};
3131

32-
static_assert(__builtin_is_cpp_trivially_relocatable(MultipleMembers));
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));
3340

3441
struct UnionOfPtrauth {
3542
union {

0 commit comments

Comments
 (0)