Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -629,10 +629,27 @@ class ASTContext : public RefCountedBase<ASTContext> {
void setRelocationInfoForCXXRecord(const CXXRecordDecl *,
CXXRecordDeclRelocationInfo);

/// Examines a given type, and returns whether the T 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);

private:
// A simple helper function to short circuit pointer auth checks.
bool isPointerAuthenticationAvailable() const {
return LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics ||
LangOpts.PointerAuthVTPtrAddressDiscrimination;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 questions; Why do we have 3 options, and why do we care about anything but PointerAuthVTPtrAddressDiscriminationhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first is historical sadness, we can probably make this better when I move the pointer auth options and similar into the target info rather than the obtuse combination of codegen and langopts we currently have. When we do that we can probably just have a single "the target supports pointer auth" flag which is what this is trying to approximate.

The reason that PointerAuthVTPtrAddressDiscrimination is not sufficient is that there is a qualifier that can be specified explicitly.

That said as we discussed (for people following at home @cor3ntin and I are talking at wg21) if someone had PointerAuthVTPtrAddressDiscrimination enabled without PointerAuthCalls and PointerAuthIntrinsics would likely be in a world of hurt, so I'll remove that separate check.


llvm::DenseMap<const CXXRecordDecl *, CXXRecordDeclRelocationInfo>
RelocatableClasses;

// FIXME: store in RecordDeclBitfields in future?
llvm::DenseMap<const RecordDecl *, bool>
RecordContainsAddressDiscriminatedPointerAuth;

ImportDecl *FirstLocalImport = nullptr;
ImportDecl *LastLocalImport = nullptr;

Expand Down Expand Up @@ -3668,6 +3685,13 @@ OPT_LIST(V)
/// authentication policy for the specified record.
const CXXRecordDecl *
baseForVTableAuthentication(const CXXRecordDecl *ThisClass);

/// If this class is polymorphic, returns true if any of this class's
/// vtable pointers have an address discriminated pointer authentication
/// schema.
/// This does not check fields of the class or base classes.
bool hasAddressDiscriminatedVTableAuthentication(const CXXRecordDecl *Class);

bool useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
StringRef MangledName);

Expand Down
51 changes: 51 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,41 @@ void ASTContext::setRelocationInfoForCXXRecord(
RelocatableClasses.insert({D, Info});
}

bool ASTContext::containsAddressDiscriminatedPointerAuth(QualType T) {
if (!isPointerAuthenticationAvailable())
return false;

T = T.getCanonicalType();
if (T.hasAddressDiscriminatedPointerAuth())
return true;
const RecordDecl *RD = T->getAsRecordDecl();
if (!RD)
return false;

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

auto SaveReturn = [this, RD](bool Result) {
RecordContainsAddressDiscriminatedPointerAuth.insert({RD, Result});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
RecordContainsAddressDiscriminatedPointerAuth.insert({RD, Result});
RecordContainsAddressDiscriminatedPointerAuth.emplace(RD, Result);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move SaveReturn after the map lookup?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

llvm's map does not have emplace afaict

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hum, sorry, try_emplace

return Result;
};
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");
Expand Down Expand Up @@ -15121,6 +15156,22 @@ ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) {
return PrimaryBase;
}

bool ASTContext::hasAddressDiscriminatedVTableAuthentication(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that can just be a (static) function declared in this file, taking ASTContext as parameter - it should not be called directly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which function?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasAddressDiscriminatedVTableAuthentication

const CXXRecordDecl *Class) {
if (!isPointerAuthenticationAvailable() || !Class->isPolymorphic())
return false;
const CXXRecordDecl *BaseType = baseForVTableAuthentication(Class);
using AuthAttr = VTablePointerAuthenticationAttr;
const AuthAttr *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());
Expand Down
16 changes: 11 additions & 5 deletions clang/lib/Sema/SemaTypeTraits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -313,7 +320,6 @@ bool Sema::IsCXXTriviallyRelocatableType(const CXXRecordDecl &RD) {
}

bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {

QualType BaseElementType = getASTContext().getBaseElementType(Type);

if (Type->isVariableArrayType())
Expand All @@ -322,9 +328,6 @@ bool Sema::IsCXXTriviallyRelocatableType(QualType Type) {
if (BaseElementType.hasNonTrivialObjCLifetime())
return false;

if (BaseElementType.hasAddressDiscriminatedPointerAuth())
return false;

if (BaseElementType->isIncompleteType())
return false;

Expand Down Expand Up @@ -670,7 +673,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();
Expand Down
1 change: 1 addition & 0 deletions clang/test/SemaCXX/cxx2c-trivially-relocatable.cpp
Original file line number Diff line number Diff line change
@@ -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));
Expand Down
52 changes: 43 additions & 9 deletions clang/test/SemaCXX/ptrauth-triviality.cpp
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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>));
Expand All @@ -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 {
Expand Down Expand Up @@ -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>));

Expand All @@ -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));

Expand All @@ -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));

Expand All @@ -148,7 +146,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>));
Expand All @@ -157,7 +155,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 {
Expand All @@ -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>>>);
102 changes: 102 additions & 0 deletions clang/test/SemaCXX/trivially-relocatable-ptrauth.cpp
Original file line number Diff line number Diff line change
@@ -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));
Loading