diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 8d24d393eab09..95312b9168d24 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -629,10 +629,48 @@ class ASTContext : public RefCountedBase { void setRelocationInfoForCXXRecord(const CXXRecordDecl *, CXXRecordDeclRelocationInfo); + /// Examines a given type, and returns whether the type itself + /// is address discriminated, or any transitively embedded types + /// contain data that is address discriminated. This includes + /// implicitly authenticated values like vtable pointers, as well as + /// explicitly qualified fields. + bool containsAddressDiscriminatedPointerAuth(QualType T) { + if (!isPointerAuthenticationAvailable()) + return false; + return findPointerAuthContent(T) != PointerAuthContent::None; + } + + /// Examines a given type, and returns whether the type itself + /// or any data it transitively contains has a pointer authentication + /// schema that is not safely relocatable. e.g. any data or fields + /// with address discrimination other than any otherwise similar + /// vtable pointers. + bool containsNonRelocatablePointerAuth(QualType T) { + if (!isPointerAuthenticationAvailable()) + return false; + return findPointerAuthContent(T) == + PointerAuthContent::AddressDiscriminatedData; + } + private: llvm::DenseMap RelocatableClasses; + // FIXME: store in RecordDeclBitfields in future? + enum class PointerAuthContent : uint8_t { + None, + AddressDiscriminatedVTable, + AddressDiscriminatedData + }; + + // A simple helper function to short circuit pointer auth checks. + bool isPointerAuthenticationAvailable() const { + return LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics; + } + PointerAuthContent findPointerAuthContent(QualType T); + llvm::DenseMap + RecordContainsAddressDiscriminatedPointerAuth; + ImportDecl *FirstLocalImport = nullptr; ImportDecl *LastLocalImport = nullptr; @@ -3668,6 +3706,7 @@ OPT_LIST(V) /// authentication policy for the specified record. const CXXRecordDecl * baseForVTableAuthentication(const CXXRecordDecl *ThisClass); + bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl, StringRef MangledName); diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index b51f7622288df..b2516d7f2e6fc 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1705,6 +1705,73 @@ void ASTContext::setRelocationInfoForCXXRecord( RelocatableClasses.insert({D, Info}); } +static bool primaryBaseHaseAddressDiscriminatedVTableAuthentication( + ASTContext &Context, const CXXRecordDecl *Class) { + if (!Class->isPolymorphic()) + return false; + const CXXRecordDecl *BaseType = Context.baseForVTableAuthentication(Class); + using AuthAttr = VTablePointerAuthenticationAttr; + const AuthAttr *ExplicitAuth = BaseType->getAttr(); + if (!ExplicitAuth) + return Context.getLangOpts().PointerAuthVTPtrAddressDiscrimination; + AuthAttr::AddressDiscriminationMode AddressDiscrimination = + ExplicitAuth->getAddressDiscrimination(); + if (AddressDiscrimination == AuthAttr::DefaultAddressDiscrimination) + return Context.getLangOpts().PointerAuthVTPtrAddressDiscrimination; + return AddressDiscrimination == AuthAttr::AddressDiscrimination; +} + +ASTContext::PointerAuthContent ASTContext::findPointerAuthContent(QualType T) { + assert(isPointerAuthenticationAvailable()); + + T = T.getCanonicalType(); + if (T.hasAddressDiscriminatedPointerAuth()) + return PointerAuthContent::AddressDiscriminatedData; + const RecordDecl *RD = T->getAsRecordDecl(); + if (!RD) + return PointerAuthContent::None; + + if (auto Existing = RecordContainsAddressDiscriminatedPointerAuth.find(RD); + Existing != RecordContainsAddressDiscriminatedPointerAuth.end()) + return Existing->second; + + PointerAuthContent Result = PointerAuthContent::None; + + auto SaveResultAndReturn = [&]() -> PointerAuthContent { + auto [ResultIter, DidAdd] = + RecordContainsAddressDiscriminatedPointerAuth.try_emplace(RD, Result); + (void)ResultIter; + (void)DidAdd; + assert(DidAdd); + return Result; + }; + auto ShouldContinueAfterUpdate = [&](PointerAuthContent NewResult) { + static_assert(PointerAuthContent::None < + PointerAuthContent::AddressDiscriminatedVTable); + static_assert(PointerAuthContent::AddressDiscriminatedVTable < + PointerAuthContent::AddressDiscriminatedData); + if (NewResult > Result) + Result = NewResult; + return Result != PointerAuthContent::AddressDiscriminatedData; + }; + if (const CXXRecordDecl *CXXRD = dyn_cast(RD)) { + if (primaryBaseHaseAddressDiscriminatedVTableAuthentication(*this, CXXRD) && + !ShouldContinueAfterUpdate( + PointerAuthContent::AddressDiscriminatedVTable)) + return SaveResultAndReturn(); + for (auto Base : CXXRD->bases()) { + if (!ShouldContinueAfterUpdate(findPointerAuthContent(Base.getType()))) + return SaveResultAndReturn(); + } + } + for (auto *FieldDecl : RD->fields()) { + if (!ShouldContinueAfterUpdate( + findPointerAuthContent(FieldDecl->getType()))) + return SaveResultAndReturn(); + } + return SaveResultAndReturn(); +} + void ASTContext::addedLocalImportDecl(ImportDecl *Import) { assert(!Import->getNextLocalImport() && "Import declaration already in the chain"); diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp index 1738ab4466001..4dbb2450857e0 100644 --- a/clang/lib/Sema/SemaTypeTraits.cpp +++ b/clang/lib/Sema/SemaTypeTraits.cpp @@ -188,6 +188,7 @@ static bool IsEligibleForTrivialRelocation(Sema &SemaRef, return false; } + bool IsUnion = D->isUnion(); for (const FieldDecl *Field : D->fields()) { if (Field->getType()->isDependentType()) continue; @@ -197,6 +198,12 @@ static bool IsEligibleForTrivialRelocation(Sema &SemaRef, // of a trivially relocatable type if (!SemaRef.IsCXXTriviallyRelocatableType(Field->getType())) return false; + + // A union contains values with address discriminated pointer auth + // cannot be relocated. + if (IsUnion && SemaRef.Context.containsAddressDiscriminatedPointerAuth( + Field->getType())) + return false; } return !D->hasDeletedDestructor(); } @@ -313,7 +320,6 @@ bool Sema::IsCXXTriviallyRelocatableType(const CXXRecordDecl &RD) { } bool Sema::IsCXXTriviallyRelocatableType(QualType Type) { - QualType BaseElementType = getASTContext().getBaseElementType(Type); if (Type->isVariableArrayType()) @@ -322,10 +328,10 @@ bool Sema::IsCXXTriviallyRelocatableType(QualType Type) { if (BaseElementType.hasNonTrivialObjCLifetime()) return false; - if (BaseElementType.hasAddressDiscriminatedPointerAuth()) + if (BaseElementType->isIncompleteType()) return false; - if (BaseElementType->isIncompleteType()) + if (Context.containsNonRelocatablePointerAuth(Type)) return false; if (BaseElementType->isScalarType() || BaseElementType->isVectorType()) @@ -670,7 +676,10 @@ static bool IsTriviallyRelocatableType(Sema &SemaRef, QualType T) { if (!BaseElementType->isObjectType()) return false; - if (T.hasAddressDiscriminatedPointerAuth()) + // The deprecated __builtin_is_trivially_relocatable does not have + // an equivalent to __builtin_trivially_relocate, so there is no + // safe way to use it if there are any address discriminated values. + 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..ba8a8273d5c05 100644 --- a/clang/test/SemaCXX/ptrauth-triviality.cpp +++ b/clang/test/SemaCXX/ptrauth-triviality.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++20 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s -// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++20 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s +// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++26 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++26 -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s #define AQ __ptrauth(1,1,50) #define IQ __ptrauth(1,0,50) @@ -83,7 +83,7 @@ static_assert(!__is_trivially_constructible(Holder, const Holder&)); static_assert(!__is_trivially_assignable(Holder, const Holder&)); static_assert(__is_trivially_destructible(Holder)); static_assert(!__is_trivially_copyable(Holder)); -static_assert(__is_trivially_relocatable(Holder)); // expected-warning{{deprecated}} +static_assert(!__is_trivially_relocatable(Holder)); // expected-warning{{deprecated}} static_assert(__builtin_is_cpp_trivially_relocatable(Holder)); static_assert(!__is_trivially_equality_comparable(Holder)); @@ -99,7 +99,6 @@ static_assert(!__is_trivially_assignable(S4, const S4&)); static_assert(__is_trivially_destructible(S4)); static_assert(!__is_trivially_copyable(S4)); static_assert(!__is_trivially_relocatable(S4)); // expected-warning{{deprecated}} -//FIXME static_assert(__builtin_is_cpp_trivially_relocatable(S4)); static_assert(!__is_trivially_equality_comparable(S4)); @@ -124,7 +123,6 @@ static_assert(!__is_trivially_assignable(S5, const S5&)); static_assert(__is_trivially_destructible(S5)); static_assert(!__is_trivially_copyable(S5)); static_assert(!__is_trivially_relocatable(S5)); // expected-warning{{deprecated}} -//FIXME static_assert(__builtin_is_cpp_trivially_relocatable(S5)); static_assert(!__is_trivially_equality_comparable(S5)); @@ -182,3 +180,39 @@ static_assert(__is_trivially_copyable(Holder)); static_assert(__is_trivially_relocatable(Holder)); // expected-warning{{deprecated}} static_assert(__builtin_is_cpp_trivially_relocatable(Holder)); static_assert(__is_trivially_equality_comparable(Holder)); + +template struct MultipleInheriter : Bases... { +}; + +template static const bool test_is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T); +template static const bool multiple_inheritance_is_relocatable = test_is_trivially_relocatable_v>; +template static const bool inheritance_relocatability_matches_bases_v = + (test_is_trivially_relocatable_v && ...) == multiple_inheritance_is_relocatable; + +static_assert(multiple_inheritance_is_relocatable == multiple_inheritance_is_relocatable); +static_assert(inheritance_relocatability_matches_bases_v); +static_assert(inheritance_relocatability_matches_bases_v); + +struct AA AddressDiscriminatedPolymorphicBase trivially_relocatable_if_eligible { + virtual void foo(); +}; + +struct IA NoAddressDiscriminatedPolymorphicBase trivially_relocatable_if_eligible { + virtual void bar(); +}; + +template struct UnionWrapper trivially_relocatable_if_eligible { + union U { + T field1; + } u; +}; + +static_assert(test_is_trivially_relocatable_v); +static_assert(test_is_trivially_relocatable_v); +static_assert(inheritance_relocatability_matches_bases_v); +static_assert(inheritance_relocatability_matches_bases_v); + +static_assert(!test_is_trivially_relocatable_v>); +static_assert(test_is_trivially_relocatable_v>); +static_assert(!test_is_trivially_relocatable_v>>); +static_assert(!test_is_trivially_relocatable_v>>); diff --git a/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp b/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp new file mode 100644 index 0000000000000..b38499a634fcf --- /dev/null +++ b/clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp @@ -0,0 +1,109 @@ +// 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 MultipleMembers1 { + NonAddressDiscPtrauth field0; + AddressDiscPtrauth field1; +}; + +static_assert(!__builtin_is_cpp_trivially_relocatable(MultipleMembers1)); + +struct MultipleMembers2 { + NonAddressDiscPtrauth field0; + NonAddressDiscPtrauth field1; +}; + +static_assert(__builtin_is_cpp_trivially_relocatable(MultipleMembers2)); + +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));