-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang] Fix PointerAuth semantics of cpp_trivially_relocatable #143969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang] Fix PointerAuth semantics of cpp_trivially_relocatable #143969
Conversation
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.
@llvm/pr-subscribers-clang Author: Oliver Hunt (ojhunt) ChangesThis 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:
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));
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo nits
clang/lib/AST/ASTContext.cpp
Outdated
@@ -15121,6 +15155,21 @@ ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) { | |||
return PrimaryBase; | |||
} | |||
|
|||
bool ASTContext::hasAddressDiscriminatedVTableAuthentication( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hasAddressDiscriminatedVTableAuthentication
clang/lib/AST/ASTContext.cpp
Outdated
return false; | ||
|
||
auto SaveReturn = [this, RD](bool Result) { | ||
RecordContainsAddressDiscriminatedPointerAuth.insert({RD, Result}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RecordContainsAddressDiscriminatedPointerAuth.insert({RD, Result}); | |
RecordContainsAddressDiscriminatedPointerAuth.emplace(RD, Result); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hum, sorry, try_emplace
✅ With the latest revision this PR passed the C/C++ code formatter. |
286c39e
to
3190a52
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
goddammit review comment mode :D
clang/lib/AST/ASTContext.cpp
Outdated
@@ -15121,6 +15155,21 @@ ASTContext::baseForVTableAuthentication(const CXXRecordDecl *ThisClass) { | |||
return PrimaryBase; | |||
} | |||
|
|||
bool ASTContext::hasAddressDiscriminatedVTableAuthentication( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which function?
clang/lib/AST/ASTContext.cpp
Outdated
return false; | ||
|
||
auto SaveReturn = [this, RD](bool Result) { | ||
RecordContainsAddressDiscriminatedPointerAuth.insert({RD, Result}); |
There was a problem hiding this comment.
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
Very glad I didn't just assume my local build was sufficient and waited for the bots :D |
Actually I've given this a lot of thought, I think we should not consider explicitly __ptrauth qualified types to be relocatable, how does that sound? |
found how to unmark the approval as the next update will change this fairly substantially |
After consideration, while "supportable" by relocation, it seems much more reasonable for people to understand that explicitly qualified types can have different relocatability semantics. Implicitly address discriminated fields should be relocatable as developers in general cannot control those behaviours, hence the trivial_relocation language explicitly acknowledges that this impacts unions. Explicitly address discriminated fields do however drastically change the developer aware model of a type, and the practical requirements for efficiently handling __ptrauth qualified types mean that you would want some_addr_discriminated_t[100]; to not be reported as trivially relocatable, while struct { ... some_addr_discriminated_t field; ... } to be reported as such, and the actual decision would regress to what proportion of the object being relocated is subject to such a policy. Having is_trivially_relocatable change behaviour according to some hypothetical internal heuristic is clearly a terrible idea, and so I think for now we should instead simply limit the application of trivial relocation to address discriminated data to those fields that outside of the control of developers.
clang/include/clang/AST/ASTContext.h
Outdated
bool isPointerAuthenticationAvailable() const { | ||
return LangOpts.PointerAuthCalls || LangOpts.PointerAuthIntrinsics || | ||
LangOpts.PointerAuthVTPtrAddressDiscrimination; | ||
} |
There was a problem hiding this comment.
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 PointerAuthVTPtrAddressDiscrimination
here?
There was a problem hiding this comment.
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.
bool containsAddressDiscriminatedPointerAuth(QualType T) { | ||
if (!isPointerAuthenticationAvailable()) | ||
return false; | ||
return findPointerAuthContent(T) != PointerAuthContent::None; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that still used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, it's possible I updated it without verifying it was needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right, this is used for the union check - in a union it does not matter what the source of the address discrimination is, a union containing anything address discriminated isn't valid. In principle with this change the only thing that would get here is a vtable pointer but it seems reasonable to be safe.
For people following at home, Oliver and I discussed this offline and we both agreed that enabling of relocation of discriminated vtable was a good first approach. we could always explore ways to extend what we support later, in a way that doesn't confuse people who try to relocate large arrays of discriminated pointers. The traits need to reflect what codegen supports, and Codegen should be worked on iteratively. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/157/builds/30880 Here is the relevant piece of the build log for the reference
|
I'm not convinced a change to trivial relocation in c++ is the cause of failure in a flang test? |
…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 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.