[clang] [clang] Fix PointerAuth semantics of cpp_trivially_relocatable (PR #143969)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 12 13:52:38 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Oliver Hunt (ojhunt)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/143969.diff
6 Files Affected:
- (modified) clang/include/clang/AST/ASTContext.h (+4)
- (modified) clang/lib/AST/ASTContext.cpp (+49)
- (modified) clang/lib/Sema/SemaTypeTraits.cpp (+8-6)
- (modified) clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp (+1)
- (modified) clang/test/SemaCXX/ptrauth-triviality.cpp (+5-5)
- (added) clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp (+102)
``````````diff
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 8d24d393eab09..826f5257b0463 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -628,10 +628,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
getRelocationInfoForCXXRecord(const CXXRecordDecl *) const;
void setRelocationInfoForCXXRecord(const CXXRecordDecl *,
CXXRecordDeclRelocationInfo);
+ bool containsAddressDiscriminatedPointerAuth(QualType T);
private:
llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo>
RelocatableClasses;
+ llvm::DenseMap<const RecordDecl *, bool>
+ RecordContainsAddressDiscriminatedPointerAuth;
ImportDecl *FirstLocalImport = nullptr;
ImportDecl *LastLocalImport = nullptr;
@@ -3668,6 +3671,7 @@ OPT_LIST(V)
/// authentication policy for the specified record.
const CXXRecordDecl *
baseForVTableAuthentication(const CXXRecordDecl *ThisClass);
+ bool hasAddressDiscriminatedVTableAuthentication(const CXXRecordDecl *Class);
bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
StringRef MangledName);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b51f7622288df..34b540fd36efc 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1705,6 +1705,40 @@ void ASTContext::setRelocationInfoForCXXRecord(
RelocatableClasses.insert({D, Info});
}
+bool ASTContext::containsAddressDiscriminatedPointerAuth(QualType T) {
+ if (!LangOpts.PointerAuthCalls && !LangOpts.PointerAuthIntrinsics)
+ return false;
+
+ T = T.getCanonicalType();
+ if (T.hasAddressDiscriminatedPointerAuth())
+ return true;
+ const RecordDecl *RD = T->getAsRecordDecl();
+ if (!RD)
+ return false;
+
+ auto SaveReturn = [this, RD](bool Result) {
+ RecordContainsAddressDiscriminatedPointerAuth.insert({RD, Result});
+ return Result;
+ };
+ if (auto Existing = RecordContainsAddressDiscriminatedPointerAuth.find(RD);
+ Existing != RecordContainsAddressDiscriminatedPointerAuth.end())
+ return Existing->second;
+ if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ if (CXXRD->isPolymorphic() &&
+ hasAddressDiscriminatedVTableAuthentication(CXXRD))
+ return SaveReturn(true);
+ for (auto Base : CXXRD->bases()) {
+ if (containsAddressDiscriminatedPointerAuth(Base.getType()))
+ return SaveReturn(true);
+ }
+ }
+ for (auto *FieldDecl : RD->fields()) {
+ if (containsAddressDiscriminatedPointerAuth(FieldDecl->getType()))
+ return SaveReturn(true);
+ }
+ return SaveReturn(false);
+}
+
void ASTContext::addedLocalImportDecl(ImportDecl *Import) {
assert(!Import->getNextLocalImport() &&
"Import declaration already in the chain");
@@ -15121,6 +15155,21 @@ ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) {
return PrimaryBase;
}
+bool ASTContext::hasAddressDiscriminatedVTableAuthentication(
+ const CXXRecordDecl *Class) {
+ assert(Class->isPolymorphic());
+ const CXXRecordDecl *BaseType = baseForVTableAuthentication(Class);
+ using AuthAttr = VTablePointerAuthenticationAttr;
+ const auto *ExplicitAuth = BaseType->getAttr<AuthAttr>();
+ if (!ExplicitAuth)
+ return LangOpts.PointerAuthVTPtrAddressDiscrimination;
+ AuthAttr::AddressDiscriminationMode AddressDiscrimination =
+ ExplicitAuth->getAddressDiscrimination();
+ if (AddressDiscrimination == AuthAttr::DefaultAddressDiscrimination)
+ return LangOpts.PointerAuthVTPtrAddressDiscrimination;
+ return AddressDiscrimination == AuthAttr::AddressDiscrimination;
+}
+
bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
StringRef MangledName) {
auto *Method = cast<CXXMethodDecl>(VirtualMethodDecl.getDecl());
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 1738ab4466001..43af236068655 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -188,15 +188,20 @@ static bool IsEligibleForTrivialRelocation(Sema &SemaRef,
return false;
}
+ bool IsUnion = D->isUnion();
for (const FieldDecl *Field : D->fields()) {
- if (Field->getType()->isDependentType())
+ QualType FieldType = Field->getType();
+ if (FieldType->isDependentType())
continue;
- if (Field->getType()->isReferenceType())
+ if (FieldType->isReferenceType())
continue;
// ... has a non-static data member of an object type that is not
// of a trivially relocatable type
if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType()))
return false;
+ if (IsUnion &&
+ SemaRef.Context.containsAddressDiscriminatedPointerAuth(FieldType))
+ return false;
}
return !D->hasDeletedDestructor();
}
@@ -322,9 +327,6 @@ bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {
if (BaseElementType.hasNonTrivialObjCLifetime())
return false;
- if (BaseElementType.hasAddressDiscriminatedPointerAuth())
- return false;
-
if (BaseElementType->isIncompleteType())
return false;
@@ -670,7 +672,7 @@ static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) {
if (!BaseElementType->isObjectType())
return false;
- if (T.hasAddressDiscriminatedPointerAuth())
+ if (SemaRef.getASTContext().containsAddressDiscriminatedPointerAuth(T))
return false;
if (const auto *RD = BaseElementType->getAsCXXRecordDecl();
diff --git a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
index 9d43994ee7661..7152a5937d9b7 100644
--- a/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
+++ b/clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++2c -verify %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-intrinsics -fptrauth-calls -std=c++2c -verify %s
class Trivial {};
static_assert(__builtin_is_cpp_trivially_relocatable(Trivial));
diff --git a/clang/test/SemaCXX/ptrauth-triviality.cpp b/clang/test/SemaCXX/ptrauth-triviality.cpp
index 60d1b57230f18..6f3650f7ac2e3 100644
--- a/clang/test/SemaCXX/ptrauth-triviality.cpp
+++ b/clang/test/SemaCXX/ptrauth-triviality.cpp
@@ -26,7 +26,7 @@ static_assert(!__is_trivially_assignable(S1, const S1&));
static_assert(__is_trivially_destructible(S1));
static_assert(!__is_trivially_copyable(S1));
static_assert(!__is_trivially_relocatable(S1)); // expected-warning{{deprecated}}
-static_assert(!__builtin_is_cpp_trivially_relocatable(S1));
+static_assert(__builtin_is_cpp_trivially_relocatable(S1));
static_assert(!__is_trivially_equality_comparable(S1));
static_assert(__is_trivially_constructible(Holder<S1>));
@@ -35,7 +35,7 @@ static_assert(!__is_trivially_assignable(Holder<S1>, const Holder<S1>&));
static_assert(__is_trivially_destructible(Holder<S1>));
static_assert(!__is_trivially_copyable(Holder<S1>));
static_assert(!__is_trivially_relocatable(Holder<S1>)); // expected-warning{{deprecated}}
-static_assert(!__builtin_is_cpp_trivially_relocatable(Holder<S1>));
+static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S1>));
static_assert(!__is_trivially_equality_comparable(Holder<S1>));
struct S2 {
@@ -83,7 +83,7 @@ static_assert(!__is_trivially_constructible(Holder<S3>, const Holder<S3>&));
static_assert(!__is_trivially_assignable(Holder<S3>, const Holder<S3>&));
static_assert(__is_trivially_destructible(Holder<S3>));
static_assert(!__is_trivially_copyable(Holder<S3>));
-static_assert(__is_trivially_relocatable(Holder<S3>)); // expected-warning{{deprecated}}
+static_assert(!__is_trivially_relocatable(Holder<S3>)); // expected-warning{{deprecated}}
static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S3>));
static_assert(!__is_trivially_equality_comparable(Holder<S3>));
@@ -148,7 +148,7 @@ static_assert(!__is_trivially_assignable(S6, const S6&));
static_assert(__is_trivially_destructible(S6));
static_assert(!__is_trivially_copyable(S6));
static_assert(!__is_trivially_relocatable(S6)); // expected-warning{{deprecated}}
-static_assert(!__builtin_is_cpp_trivially_relocatable(S6));
+static_assert(__builtin_is_cpp_trivially_relocatable(S6));
static_assert(!__is_trivially_equality_comparable(S6));
static_assert(__is_trivially_constructible(Holder<S6>));
@@ -157,7 +157,7 @@ static_assert(!__is_trivially_assignable(Holder<S6>, const Holder<S6>&));
static_assert(__is_trivially_destructible(Holder<S6>));
static_assert(!__is_trivially_copyable(Holder<S6>));
static_assert(!__is_trivially_relocatable(Holder<S6>)); // expected-warning{{deprecated}}
-static_assert(!__builtin_is_cpp_trivially_relocatable(Holder<S6>));
+static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S6>));
static_assert(!__is_trivially_equality_comparable(Holder<S6>));
struct S7 {
diff --git a/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp b/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp
new file mode 100644
index 0000000000000..29722fadd4d17
--- /dev/null
+++ b/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp
@@ -0,0 +1,102 @@
+// RUN: %clang_cc1 -triple arm64 -fptrauth-calls -fptrauth-intrinsics -std=c++26 -verify %s
+
+// This test intentionally does not enable the global address discrimination
+// of vtable pointers. This lets us configure them with different schemas
+// and verify that we're correctly tracking the existence of address discrimination
+
+// expected-no-diagnostics
+
+struct NonAddressDiscPtrauth {
+ void * __ptrauth(1, 0, 1234) p;
+};
+
+static_assert(__builtin_is_cpp_trivially_relocatable(NonAddressDiscPtrauth));
+
+struct AddressDiscPtrauth {
+ void * __ptrauth(1, 1, 1234) p;
+};
+
+static_assert(__builtin_is_cpp_trivially_relocatable(AddressDiscPtrauth));
+
+struct MultipleBaseClasses : NonAddressDiscPtrauth, AddressDiscPtrauth {
+
+};
+
+static_assert(__builtin_is_cpp_trivially_relocatable(MultipleBaseClasses));
+
+struct MultipleMembers {
+ NonAddressDiscPtrauth field0;
+ AddressDiscPtrauth field1;
+};
+
+static_assert(__builtin_is_cpp_trivially_relocatable(MultipleMembers));
+
+struct UnionOfPtrauth {
+ union {
+ NonAddressDiscPtrauth field0;
+ AddressDiscPtrauth field1;
+ } u;
+};
+
+static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfPtrauth));
+
+struct [[clang::ptrauth_vtable_pointer(process_independent,address_discrimination,no_extra_discrimination)]] Polymorphic trivially_relocatable_if_eligible {
+ virtual ~Polymorphic();
+};
+
+struct Foo : Polymorphic {
+ Foo(const Foo&);
+ ~Foo();
+};
+
+
+static_assert(__builtin_is_cpp_trivially_relocatable(Polymorphic));
+
+struct [[clang::ptrauth_vtable_pointer(process_independent,no_address_discrimination,no_extra_discrimination)]] NonAddressDiscriminatedPolymorphic trivially_relocatable_if_eligible {
+ virtual ~NonAddressDiscriminatedPolymorphic();
+};
+
+static_assert(__builtin_is_cpp_trivially_relocatable(NonAddressDiscriminatedPolymorphic));
+
+
+struct PolymorphicMembers {
+ Polymorphic field;
+};
+
+static_assert(__builtin_is_cpp_trivially_relocatable(PolymorphicMembers));
+
+struct UnionOfPolymorphic {
+ union trivially_relocatable_if_eligible {
+ Polymorphic p;
+ int i;
+ } u;
+};
+
+static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfPolymorphic));
+
+
+struct UnionOfNonAddressDiscriminatedPolymorphic {
+ union trivially_relocatable_if_eligible {
+ NonAddressDiscriminatedPolymorphic p;
+ int i;
+ } u;
+};
+static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfNonAddressDiscriminatedPolymorphic));
+
+struct UnionOfNonAddressDiscriminatedPtrauth {
+ union {
+ NonAddressDiscPtrauth p;
+ int i;
+ } u;
+};
+
+static_assert(__builtin_is_cpp_trivially_relocatable(UnionOfNonAddressDiscriminatedPtrauth));
+
+struct UnionOfAddressDisriminatedPtrauth {
+ union {
+ AddressDiscPtrauth p;
+ int i;
+ } u;
+};
+
+static_assert(!__builtin_is_cpp_trivially_relocatable(UnionOfAddressDisriminatedPtrauth));
``````````
</details>
https://github.com/llvm/llvm-project/pull/143969
More information about the cfe-commits
mailing list