https://github.com/ojhunt created https://github.com/llvm/llvm-project/pull/143969
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. >From f47680310ed68a9f8e8cb15bc1cd474740072463 Mon Sep 17 00:00:00 2001 From: Oliver Hunt <oli...@apple.com> Date: Thu, 12 Jun 2025 13:17:11 -0700 Subject: [PATCH] [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. --- clang/include/clang/AST/ASTContext.h | 4 + clang/lib/AST/ASTContext.cpp | 49 +++++++++ clang/lib/Sema/SemaTypeTraits.cpp | 14 +-- .../SemaCXX/cxx2c-trivially-relocatable.cpp | 1 + clang/test/SemaCXX/ptrauth-triviality.cpp | 10 +- .../SemaCXX/trivially-relocatable-ptrauth.cpp | 102 ++++++++++++++++++ 6 files changed, 169 insertions(+), 11 deletions(-) create mode 100644 clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp 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)); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits