[clang] Fix PointerAuth semantics of cpp_trivially_relocatable (#143969)
This adds a number of functions to ASTContext to query whether a type contains data protected with address discriminated pointer authentication, and whether the protected values are just vtable pointers, or if there are other address discriminated types included. For the standardized version, __builtin_is_cpp_trivially_relocatable this means accepting types where the only address discriminated values are vtable pointers. Other address discriminated types are not considered relocatable. In addition to that any union containing any address discriminated data, including vtable pointers, is not relocatable. For the old deprecated __builtin_is_trivially_relocatable we reject any type containing any address discriminated value, as it is semantically intended as being a "is this memcopyable" which is not true for anything with address discrimination. This PR does not update the codegen for __builtin_trivially_relocate, that will be in a follow on PR that is much more complex.
This commit is contained in:
parent
79a2b15a4c
commit
eddab9b757
@ -629,10 +629,48 @@ public:
|
||||
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<const CXXRecordDecl *, CXXRecordDeclRelocationInfo>
|
||||
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<const RecordDecl *, PointerAuthContent>
|
||||
RecordContainsAddressDiscriminatedPointerAuth;
|
||||
|
||||
ImportDecl *FirstLocalImport = nullptr;
|
||||
ImportDecl *LastLocalImport = nullptr;
|
||||
|
||||
@ -3668,6 +3706,7 @@ public:
|
||||
/// authentication policy for the specified record.
|
||||
const CXXRecordDecl *
|
||||
baseForVTableAuthentication(const CXXRecordDecl *ThisClass);
|
||||
|
||||
bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
|
||||
StringRef MangledName);
|
||||
|
||||
|
@ -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<AuthAttr>();
|
||||
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<CXXRecordDecl>(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");
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
|
@ -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<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>));
|
||||
|
||||
@ -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<S7>));
|
||||
static_assert(__is_trivially_relocatable(Holder<S7>)); // expected-warning{{deprecated}}
|
||||
static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S7>));
|
||||
static_assert(__is_trivially_equality_comparable(Holder<S7>));
|
||||
|
||||
template <class... Bases> struct MultipleInheriter : Bases... {
|
||||
};
|
||||
|
||||
template <class T> static const bool test_is_trivially_relocatable_v = __builtin_is_cpp_trivially_relocatable(T);
|
||||
template <class... Types> static const bool multiple_inheritance_is_relocatable = test_is_trivially_relocatable_v<MultipleInheriter<Types...>>;
|
||||
template <class... Types> static const bool inheritance_relocatability_matches_bases_v =
|
||||
(test_is_trivially_relocatable_v<Types> && ...) == multiple_inheritance_is_relocatable<Types...>;
|
||||
|
||||
static_assert(multiple_inheritance_is_relocatable<S4, S5> == multiple_inheritance_is_relocatable<S5, S4>);
|
||||
static_assert(inheritance_relocatability_matches_bases_v<S4, S5>);
|
||||
static_assert(inheritance_relocatability_matches_bases_v<S5, S4>);
|
||||
|
||||
struct AA AddressDiscriminatedPolymorphicBase trivially_relocatable_if_eligible {
|
||||
virtual void foo();
|
||||
};
|
||||
|
||||
struct IA NoAddressDiscriminatedPolymorphicBase trivially_relocatable_if_eligible {
|
||||
virtual void bar();
|
||||
};
|
||||
|
||||
template <class T> struct UnionWrapper trivially_relocatable_if_eligible {
|
||||
union U {
|
||||
T field1;
|
||||
} u;
|
||||
};
|
||||
|
||||
static_assert(test_is_trivially_relocatable_v<AddressDiscriminatedPolymorphicBase>);
|
||||
static_assert(test_is_trivially_relocatable_v<NoAddressDiscriminatedPolymorphicBase>);
|
||||
static_assert(inheritance_relocatability_matches_bases_v<AddressDiscriminatedPolymorphicBase, NoAddressDiscriminatedPolymorphicBase>);
|
||||
static_assert(inheritance_relocatability_matches_bases_v<NoAddressDiscriminatedPolymorphicBase, AddressDiscriminatedPolymorphicBase>);
|
||||
|
||||
static_assert(!test_is_trivially_relocatable_v<UnionWrapper<AddressDiscriminatedPolymorphicBase>>);
|
||||
static_assert(test_is_trivially_relocatable_v<UnionWrapper<NoAddressDiscriminatedPolymorphicBase>>);
|
||||
static_assert(!test_is_trivially_relocatable_v<UnionWrapper<MultipleInheriter<NoAddressDiscriminatedPolymorphicBase, AddressDiscriminatedPolymorphicBase>>>);
|
||||
static_assert(!test_is_trivially_relocatable_v<UnionWrapper<MultipleInheriter<AddressDiscriminatedPolymorphicBase, NoAddressDiscriminatedPolymorphicBase>>>);
|
||||
|
109
clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp
Normal file
109
clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp
Normal file
@ -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));
|
Loading…
x
Reference in New Issue
Block a user