[clang] [clang] Add support for `__ptrauth` being applied to integer types (PR #137580)
Oliver Hunt via cfe-commits
cfe-commits at lists.llvm.org
Fri May 9 11:49:17 PDT 2025
https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/137580
>From 07b6275740646c6ac0ffd95518e7e9fa8f1e8717 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sun, 27 Apr 2025 22:33:44 -0700
Subject: [PATCH 1/2] [clang] Add support for __ptrauth being applied to
integer types
This adds support for the __ptrauth qualifier to be applied to
pointer sized integer types.
---
clang/docs/PointerAuthentication.rst | 11 +-
clang/include/clang/AST/Type.h | 12 +-
.../clang/Basic/DiagnosticSemaKinds.td | 10 +-
clang/lib/AST/ASTContext.cpp | 3 +
clang/lib/AST/Type.cpp | 6 +
clang/lib/CodeGen/CGExprConstant.cpp | 4 +
clang/lib/CodeGen/CGExprScalar.cpp | 2 +-
clang/lib/CodeGen/CGPointerAuth.cpp | 17 +-
clang/lib/Sema/SemaDeclCXX.cpp | 2 +-
clang/lib/Sema/SemaType.cpp | 10 +-
clang/lib/Sema/TreeTransform.h | 12 +-
.../ptrauth-restricted-intptr-qualifier.c | 219 ++++++++++++++++++
clang/test/Sema/ptrauth-qualifier.c | 6 +-
clang/test/SemaCXX/ptrauth-triviality.cpp | 48 ++++
14 files changed, 329 insertions(+), 33 deletions(-)
create mode 100644 clang/test/CodeGen/ptrauth-restricted-intptr-qualifier.c
diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst
index 41818d43ac687..b9341a9c3b6a8 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -284,13 +284,14 @@ __ptrauth Qualifier
^^^^^^^^^^^^^^^^^^^
``__ptrauth(key, address, discriminator)`` is an extended type
-qualifier which causes so-qualified objects to hold pointers signed using the
-specified schema rather than the default schema for such types.
+qualifier which causes so-qualified objects to hold pointers or pointer sized
+integers signed using the specified schema rather than the default schema for
+such types.
In the current implementation in Clang, the qualified type must be a C pointer
-type, either to a function or to an object. It currently cannot be an
-Objective-C pointer type, a C++ reference type, or a block pointer type; these
-restrictions may be lifted in the future.
+type, either to a function or to an object, or a pointer sized integer. It
+currently cannot be an Objective-C pointer type, a C++ reference type, or a
+block pointer type; these restrictions may be lifted in the future.
The qualifier's operands are as follows:
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 773796a55eaa1..435a9de68f774 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -2560,7 +2560,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
bool isFunctionProtoType() const { return getAs<FunctionProtoType>(); }
bool isPointerType() const;
bool isPointerOrReferenceType() const;
- bool isSignableType() const;
+ bool isSignableType(const ASTContext &Ctx) const;
+ bool isSignablePointerType() const;
+ bool isSignableIntegerType(const ASTContext &Ctx) const;
bool isAnyPointerType() const; // Any C pointer or ObjC object pointer
bool isCountAttributedType() const;
bool isBlockPointerType() const;
@@ -8222,7 +8224,13 @@ inline bool Type::isAnyPointerType() const {
return isPointerType() || isObjCObjectPointerType();
}
-inline bool Type::isSignableType() const { return isPointerType(); }
+inline bool Type::isSignableType(const ASTContext &Ctx) const {
+ return isSignablePointerType() || isSignableIntegerType(Ctx);
+}
+
+inline bool Type::isSignablePointerType() const {
+ return isPointerType() || isObjCClassType() || isObjCQualifiedClassType();
+}
inline bool Type::isBlockPointerType() const {
return isa<BlockPointerType>(CanonicalType);
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e1b9ed0647bb9..034ef4ff57187 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1031,10 +1031,12 @@ def err_ptrauth_qualifier_invalid : Error<
"%select{return type|parameter type|property}1 may not be qualified with '__ptrauth'; type is %0">;
def err_ptrauth_qualifier_cast : Error<
"cannot cast to '__ptrauth'-qualified type %0">;
-def err_ptrauth_qualifier_nonpointer : Error<
- "'__ptrauth' qualifier only applies to pointer types; %0 is invalid">;
-def err_ptrauth_qualifier_redundant : Error<
- "type %0 is already %1-qualified">;
+def err_ptrauth_qualifier_invalid_target
+ : Error<"'__ptrauth' qualifier only applies to pointer or pointer sized "
+ "integer"
+ " types; %0 is invalid">;
+def err_ptrauth_qualifier_redundant
+ : Error<"type %0 is already __ptrauth-qualified">;
def err_ptrauth_arg_not_ice : Error<
"argument to '__ptrauth' must be an integer constant expression">;
def err_ptrauth_address_discrimination_invalid : Error<
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 1ed16748dff1a..93c77638e5310 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -2943,6 +2943,9 @@ bool ASTContext::hasUniqueObjectRepresentations(
// All integrals and enums are unique.
if (Ty->isIntegralOrEnumerationType()) {
+ // Address discriminated integer types are not unique.
+ if (Ty.hasAddressDiscriminatedPointerAuth())
+ return false;
// Except _BitInt types that have padding bits.
if (const auto *BIT = Ty->getAs<BitIntType>())
return getTypeSize(BIT) == BIT->getNumBits();
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 31e4bcd7535ea..0776d228a07ec 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5062,6 +5062,12 @@ AttributedType::stripOuterNullability(QualType &T) {
return std::nullopt;
}
+bool Type::isSignableIntegerType(const ASTContext &Ctx) const {
+ if (!isIntegralType(Ctx) || isEnumeralType())
+ return false;
+ return Ctx.getTypeSize(this) == Ctx.getTypeSize(Ctx.VoidPtrTy);
+}
+
bool Type::isBlockCompatibleObjCPointerType(ASTContext &ctx) const {
const auto *objcPtr = getAs<ObjCObjectPointerType>();
if (!objcPtr)
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index b21ebeee4bed1..ebe6bb80504b5 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2444,6 +2444,10 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType,
EnablePtrAuthFunctionTypeDiscrimination)
.tryEmit();
case APValue::Int:
+ if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth();
+ PointerAuth &&
+ (PointerAuth.authenticatesNullValues() || Value.getInt() != 0))
+ return nullptr;
return llvm::ConstantInt::get(CGM.getLLVMContext(), Value.getInt());
case APValue::FixedPoint:
return llvm::ConstantInt::get(CGM.getLLVMContext(),
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index f639a87e3ad0b..b15e4d64744ae 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2300,7 +2300,7 @@ static bool isLValueKnownNonNull(CodeGenFunction &CGF, const Expr *E) {
}
bool CodeGenFunction::isPointerKnownNonNull(const Expr *E) {
- assert(E->getType()->isSignableType());
+ assert(E->getType()->isSignableType(getContext()));
E = E->IgnoreParens();
diff --git a/clang/lib/CodeGen/CGPointerAuth.cpp b/clang/lib/CodeGen/CGPointerAuth.cpp
index 474848c8324f3..bf22912988d38 100644
--- a/clang/lib/CodeGen/CGPointerAuth.cpp
+++ b/clang/lib/CodeGen/CGPointerAuth.cpp
@@ -175,7 +175,7 @@ CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForPointeeType(QualType T) {
/// pointer type.
static CGPointerAuthInfo getPointerAuthInfoForType(CodeGenModule &CGM,
QualType PointerType) {
- assert(PointerType->isSignableType());
+ assert(PointerType->isSignableType(CGM.getContext()));
// Block pointers are currently not signed.
if (PointerType->isBlockPointerType())
@@ -209,7 +209,7 @@ emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &LV,
/// needlessly resigning the pointer.
std::pair<llvm::Value *, CGPointerAuthInfo>
CodeGenFunction::EmitOrigPointerRValue(const Expr *E) {
- assert(E->getType()->isSignableType());
+ assert(E->getType()->isSignableType(getContext()));
E = E->IgnoreParens();
if (const auto *Load = dyn_cast<ImplicitCastExpr>(E)) {
@@ -291,7 +291,10 @@ static bool equalAuthPolicies(const CGPointerAuthInfo &Left,
if (Left.isSigned() != Right.isSigned())
return false;
return Left.getKey() == Right.getKey() &&
- Left.getAuthenticationMode() == Right.getAuthenticationMode();
+ Left.getAuthenticationMode() == Right.getAuthenticationMode() &&
+ Left.isIsaPointer() == Right.isIsaPointer() &&
+ Left.authenticatesNullValues() == Right.authenticatesNullValues() &&
+ Left.getDiscriminator() == Right.getDiscriminator();
}
// Return the discriminator or return zero if the discriminator is null.
@@ -642,10 +645,10 @@ llvm::Value *CodeGenFunction::authPointerToPointerCast(llvm::Value *ResultPtr,
QualType SourceType,
QualType DestType) {
CGPointerAuthInfo CurAuthInfo, NewAuthInfo;
- if (SourceType->isSignableType())
+ if (SourceType->isSignableType(getContext()))
CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType);
- if (DestType->isSignableType())
+ if (DestType->isSignableType(getContext()))
NewAuthInfo = getPointerAuthInfoForType(CGM, DestType);
if (!CurAuthInfo && !NewAuthInfo)
@@ -667,10 +670,10 @@ Address CodeGenFunction::authPointerToPointerCast(Address Ptr,
QualType SourceType,
QualType DestType) {
CGPointerAuthInfo CurAuthInfo, NewAuthInfo;
- if (SourceType->isSignableType())
+ if (SourceType->isSignableType(getContext()))
CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType);
- if (DestType->isSignableType())
+ if (DestType->isSignableType(getContext()))
NewAuthInfo = getPointerAuthInfoForType(CGM, DestType);
if (!CurAuthInfo && !NewAuthInfo)
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 7cce7ed1fa054..cbccb567e2adf 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -10888,7 +10888,7 @@ void Sema::checkIllFormedTrivialABIStruct(CXXRecordDecl &RD) {
return;
}
- // Ill-formed if the field is an address-discriminated pointer.
+ // Ill-formed if the field is an address-discriminated value.
if (FT.hasAddressDiscriminatedPointerAuth()) {
PrintDiagAndRemoveAttr(6);
return;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 294daef70c339..a8e85c885069e 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8379,15 +8379,14 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
return;
}
- if (!T->isSignableType() && !T->isDependentType()) {
- S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_nonpointer) << T;
+ if (!T->isSignableType(Ctx) && !T->isDependentType()) {
+ S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_invalid_target) << T;
Attr.setInvalid();
return;
}
if (T.getPointerAuth()) {
- S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_redundant)
- << T << Attr.getAttrName()->getName();
+ S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_redundant) << T;
Attr.setInvalid();
return;
}
@@ -8402,7 +8401,8 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
"address discriminator arg should be either 0 or 1");
PointerAuthQualifier Qual = PointerAuthQualifier::Create(
Key, IsAddressDiscriminated, ExtraDiscriminator,
- PointerAuthenticationMode::SignAndAuth, false, false);
+ PointerAuthenticationMode::SignAndAuth, /*IsIsaPointer=*/false,
+ /*AuthenticatesNullValues=*/false);
T = S.Context.getPointerAuthType(T, Qual);
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 8b4b79c6ec039..97d62d15a2c25 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5305,13 +5305,15 @@ QualType TreeTransform<Derived>::RebuildQualifiedType(QualType T,
PointerAuthQualifier LocalPointerAuth = Quals.getPointerAuth();
if (LocalPointerAuth.isPresent()) {
if (T.getPointerAuth().isPresent()) {
- SemaRef.Diag(Loc, diag::err_ptrauth_qualifier_redundant)
- << TL.getType() << "__ptrauth";
- return QualType();
- } else if (!T->isSignableType() && !T->isDependentType()) {
- SemaRef.Diag(Loc, diag::err_ptrauth_qualifier_nonpointer) << T;
+ SemaRef.Diag(Loc, diag::err_ptrauth_qualifier_redundant) << TL.getType();
return QualType();
}
+ if (!T->isDependentType()) {
+ if (!T->isSignableType(SemaRef.getASTContext())) {
+ SemaRef.Diag(Loc, diag::err_ptrauth_qualifier_invalid_target) << T;
+ return QualType();
+ }
+ }
}
// C++ [dcl.fct]p7:
// [When] adding cv-qualifications on top of the function type [...] the
diff --git a/clang/test/CodeGen/ptrauth-restricted-intptr-qualifier.c b/clang/test/CodeGen/ptrauth-restricted-intptr-qualifier.c
new file mode 100644
index 0000000000000..dcce5764e2217
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-restricted-intptr-qualifier.c
@@ -0,0 +1,219 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -O0 -o - | FileCheck %s
+
+#define __ptrauth(...) __ptrauth(__VA_ARGS__)
+
+__INTPTR_TYPE__ __ptrauth(1, 0, 56) g1 = 0;
+// CHECK: @g1 = global i64 0
+__INTPTR_TYPE__ __ptrauth(1, 1, 1272) g2 = 0;
+// CHECK: @g2 = global i64 0
+extern __UINTPTR_TYPE__ test_int;
+__UINTPTR_TYPE__ __ptrauth(3, 1, 23) g3 = (__UINTPTR_TYPE__)&test_int;
+// CHECK: @test_int = external global i64
+// CHECK: @g3 = global i64 ptrtoint (ptr ptrauth (ptr @test_int, i32 3, i64 23, ptr @g3) to i64)
+
+__INTPTR_TYPE__ __ptrauth(1, 1, 712) ga[3] = {0,0,(__UINTPTR_TYPE__)&test_int};
+
+// CHECK: @ga = global [3 x i64] [i64 0, i64 0, i64 ptrtoint (ptr ptrauth (ptr @test_int, i32 1, i64 712, ptr getelementptr inbounds ([3 x i64], ptr @ga, i32 0, i32 2)) to i64)]
+
+struct A {
+ __INTPTR_TYPE__ __ptrauth(1, 0, 431) f0;
+ __INTPTR_TYPE__ __ptrauth(1, 0, 9182) f1;
+ __INTPTR_TYPE__ __ptrauth(1, 0, 783) f2;
+};
+
+struct A gs1 = {0, 0, (__UINTPTR_TYPE__)&test_int};
+// CHECK: @gs1 = global %struct.A { i64 0, i64 0, i64 ptrtoint (ptr ptrauth (ptr @test_int, i32 1, i64 783) to i64) }
+
+struct B {
+ __INTPTR_TYPE__ __ptrauth(1, 1, 1276) f0;
+ __INTPTR_TYPE__ __ptrauth(1, 1, 23674) f1;
+ __INTPTR_TYPE__ __ptrauth(1, 1, 163) f2;
+};
+
+struct B gs2 = {0, 0, (__UINTPTR_TYPE__)&test_int};
+// CHECK: @gs2 = global %struct.B { i64 0, i64 0, i64 ptrtoint (ptr ptrauth (ptr @test_int, i32 1, i64 163, ptr getelementptr inbounds (%struct.B, ptr @gs2, i32 0, i32 2)) to i64) }
+
+// CHECK-LABEL: i64 @test_read_globals
+__INTPTR_TYPE__ test_read_globals() {
+ __INTPTR_TYPE__ result = g1 + g2 + g3;
+ // CHECK: [[A:%.*]] = load i64, ptr @g1
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[A]], i32 1, i64 56)
+ // CHECK: [[B:%.*]] = load i64, ptr @g2
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @g2 to i64), i64 1272)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[B]], i32 1, i64 [[BLENDED]])
+ // CHECK: [[VALUE:%.*]] = load i64, ptr @g3
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @g3 to i64), i64 23)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 3, i64 [[BLENDED]])
+
+ for (int i = 0; i < 3; i++) {
+ result += ga[i];
+ }
+ // CHECK: for.cond:
+ // CHECK: [[TEMP:%.*]] = load i32, ptr [[IDX_ADDR:%.*]]
+
+ // CHECK: for.body:
+ // CHECK: [[IDX:%.*]] = load i32, ptr [[IDX_ADDR]]
+ // CHECK: [[IDXPROM:%.*]] = sext i32 [[IDX]] to i64
+ // CHECK: [[ARRAYIDX:%.*]] = getelementptr inbounds [3 x i64], ptr @ga, i64 0, i64 [[IDXPROM]]
+ // CHECK: [[VALUE:%.*]] = load i64, ptr [[ARRAYIDX]]
+ // CHECK: [[CASTIDX:%.*]] = ptrtoint ptr [[ARRAYIDX]] to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTIDX]], i64 712)
+ // CHECK: resign.nonnull6:
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]])
+ // CHECK: resign.cont7
+
+ result += gs1.f0 + gs1.f1 + gs1.f2;
+ // CHECK: resign.cont10:
+ // CHECK: [[ADDR:%.*]] = load i64, ptr getelementptr inbounds nuw (%struct.A, ptr @gs1, i32 0, i32 1
+ // CHECK: resign.nonnull11:
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[ADDR]], i32 1, i64 9182)
+ // CHECK: resign.cont12:
+ // CHECK: [[ADDR:%.*]] = load i64, ptr getelementptr inbounds nuw (%struct.A, ptr @gs1, i32 0, i32 2)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[ADDR]], i32 1, i64 783)
+ result += gs2.f0 + gs2.f1 + gs2.f2;
+ // CHECK: [[ADDR:%.*]] = load i64, ptr @gs2
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @gs2 to i64), i64 1276)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[ADDR]], i32 1, i64 [[BLENDED]])
+ // CHECK: [[ADDR:%.*]] = load i64, ptr getelementptr inbounds nuw (%struct.B, ptr @gs2, i32 0, i32 1)
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds nuw (%struct.B, ptr @gs2, i32 0, i32 1) to i64), i64 23674)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[ADDR]], i32 1, i64 [[BLENDED]])
+ // CHECK: [[ADDR:%.*]] = load i64, ptr getelementptr inbounds nuw (%struct.B, ptr @gs2, i32 0, i32 2)
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr getelementptr inbounds nuw (%struct.B, ptr @gs2, i32 0, i32 2) to i64), i64 163)
+
+ return result;
+}
+
+// CHECK-LABEL: void @test_write_globals
+void test_write_globals(int i, __INTPTR_TYPE__ j) {
+ g1 = i;
+ g2 = j;
+ g3 = 0;
+ ga[0] = i;
+ ga[1] = j;
+ ga[2] = 0;
+ gs1.f0 = i;
+ gs1.f1 = j;
+ gs1.f2 = 0;
+ gs2.f0 = i;
+ gs2.f1 = j;
+ gs2.f2 = 0;
+}
+
+// CHECK-LABEL: define void @test_set_A
+void test_set_A(struct A *a, __INTPTR_TYPE__ x, int y) {
+ a->f0 = x;
+ // CHECK: [[XADDR:%.*]] = load i64, ptr %x.addr
+ // CHECK: [[SIGNED_X:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[XADDR]], i32 1, i64 431)
+ a->f1 = y;
+ // CHECK: [[Y:%.*]] = load i32, ptr %y.addr
+ // CHECK: [[CONV:%.*]] = sext i32 [[Y]] to i64
+ // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CONV]], i32 1, i64 9182)
+ a->f2 = 0;
+ // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+ // CHECK: [[F2:%.*]] = getelementptr inbounds nuw %struct.A, ptr [[A]], i32 0, i32 2
+ // CHECK: store i64 0, ptr [[F2]]
+}
+
+// CHECK-LABEL: define void @test_set_B
+void test_set_B(struct B *b, __INTPTR_TYPE__ x, int y) {
+ b->f0 = x;
+ // CHECK: [[X:%.*]] = load i64, ptr %x.addr
+ // CHECK: [[F0_ADDR:%.*]] = ptrtoint ptr %f0 to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[F0_ADDR]], i64 1276)
+ // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[X]], i32 1, i64 [[BLENDED]])
+ b->f1 = y;
+ // CHECK: [[B:%.*]] = load ptr, ptr %b.addr
+ // CHECK: [[F1_ADDR:%.*]] = getelementptr inbounds nuw %struct.B, ptr [[B]], i32 0, i32 1
+ // CHECK: [[Y:%.*]] = load i32, ptr %y.addr, align 4
+ // CHECK: [[CONV:%.*]] = sext i32 [[Y]] to i64
+ // CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[F1_ADDR]] to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 23674)
+ // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[CONV]], i32 1, i64 [[BLENDED]])
+ b->f2 = 0;
+ // CHECK: [[B:%.*]] = load ptr, ptr %b.addr
+ // CHECK: [[F2_ADDR:%.*]] = getelementptr inbounds nuw %struct.B, ptr [[B]], i32 0, i32 2
+ // CHECK: store i64 0, ptr [[F2_ADDR]]
+}
+
+// CHECK-LABEL: define i64 @test_get_A
+__INTPTR_TYPE__ test_get_A(struct A *a) {
+ return a->f0 + a->f1 + a->f2;
+ // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+ // CHECK: [[F0_ADDR:%.*]] = getelementptr inbounds nuw %struct.A, ptr [[A]], i32 0, i32 0
+ // CHECK: [[F0:%.*]] = load i64, ptr [[F0_ADDR]]
+ // CHECK: [[AUTH:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[F0]], i32 1, i64 431)
+ // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+ // CHECK: [[F1_ADDR:%.*]] = getelementptr inbounds nuw %struct.A, ptr [[A]], i32 0, i32 1
+ // CHECK: [[F1:%.*]] = load i64, ptr [[F1_ADDR]]
+ // CHECK: [[AUTH:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[F1]], i32 1, i64 9182)
+ // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+ // CHECK: [[F2_ADDR:%.*]] = getelementptr inbounds nuw %struct.A, ptr [[A]], i32 0, i32 2
+ // CHECK: [[F2:%.*]] = load i64, ptr [[F2_ADDR]]
+ // CHECK: [[AUTH:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[F2]], i32 1, i64 783)
+}
+
+// CHECK-LABEL: define i64 @test_get_B
+__INTPTR_TYPE__ test_get_B(struct B *b) {
+ return b->f0 + b->f1 + b->f2;
+ // CHECK: [[B:%.*]] = load ptr, ptr %b.addr
+ // CHECK: [[F0:%.*]] = getelementptr inbounds nuw %struct.B, ptr [[B]], i32 0, i32 0
+ // CHECK: [[VALUE:%.*]] = load i64, ptr [[F0]]
+ // CHECK: [[CASTF0:%.*]] = ptrtoint ptr %f0 to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTF0]], i64 1276)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]])
+ // CHECK: [[B:%.*]] = load ptr, ptr %b.addr
+ // CHECK: [[ADDR:%.*]] = getelementptr inbounds nuw %struct.B, ptr [[B]], i32 0, i32 1
+ // CHECK: [[VALUE:%.*]] = load i64, ptr [[ADDR]]
+ // CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 23674)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]])
+ // CHECK: [[B:%.*]] = load ptr, ptr %b.addr
+ // CHECK: [[ADDR:%.*]] = getelementptr inbounds nuw %struct.B, ptr [[B]], i32 0, i32 2
+ // CHECK: [[VALUE:%.*]] = load i64, ptr [[ADDR]]
+ // CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr [[ADDR]] to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 163)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]])
+}
+
+// CHECK-LABEL: define void @test_resign
+void test_resign(struct A* a, const struct B *b) {
+ a->f0 = b->f0;
+ // CHECK: [[A:%.*]] = load ptr, ptr %a.addr, align 8
+ // CHECK: [[F0:%.*]] = getelementptr inbounds nuw %struct.A, ptr [[A]], i32 0, i32 0
+ // CHECK: [[B:%.*]] = load ptr, ptr %b.addr, align 8
+ // CHECK: [[F01:%.*]] = getelementptr inbounds nuw %struct.B, ptr [[B]], i32 0, i32 0
+ // CHECK: [[F01VALUE:%.*]] = load i64, ptr [[F01]]
+ // CHECK: [[CASTF01:%.*]] = ptrtoint ptr %f01 to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTF01]], i64 1276)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[F01VALUE]], i32 1, i64 [[BLENDED]], i32 1, i64 431)
+}
+
+// CHECK-LABEL: define i64 @other_test
+__INTPTR_TYPE__ other_test(__INTPTR_TYPE__ i) {
+ __INTPTR_TYPE__ __ptrauth(1, 1, 42) j = 0;
+ // CHECK: [[J_ADDR:%.*]] = ptrtoint ptr %j to i64
+ // CHECK: store i64 0, ptr %j
+ __INTPTR_TYPE__ __ptrauth(1, 1, 43) k = 1234;
+ // CHECK: [[ADDR:%.*]] = ptrtoint ptr %k to i64
+ // CHECK: [[JBLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ADDR]], i64 43)
+ // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 1234, i32 1, i64 [[JBLENDED]])
+ __INTPTR_TYPE__ __ptrauth(1, 1, 44) l = i;
+ // CHECK: [[I:%.*]] = load i64, ptr %i.addr
+ // CHECK: [[ADDR:%.*]] = ptrtoint ptr %l to i64
+ // CHECK: [[LBLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[ADDR]], i64 44)
+ // CHECK: [[SIGNED:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[I]], i32 1, i64 [[LBLENDED]])
+ asm volatile ("" ::: "memory");
+ return j + k + l;
+ // CHECK: [[VALUE:%.*]] = load i64, ptr %j
+ // CHECK: [[CAST_ADDR:%.*]] = ptrtoint ptr %j to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CAST_ADDR]], i64 42)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]])
+ // CHECK: [[VALUE:%.*]] = load i64, ptr %k
+ // CHECK: [[CASTK:%.*]] = ptrtoint ptr %k to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTK]], i64 43)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]])
+ // CHECK: [[VALUE:%.*]] = load i64, ptr %l
+ // CHECK: [[CASTL:%.*]] = ptrtoint ptr %l to i64
+ // CHECK: [[BLENDED:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[CASTL]], i64 44)
+ // CHECK: [[AUTHED:%.*]] = call i64 @llvm.ptrauth.auth(i64 [[VALUE]], i32 1, i64 [[BLENDED]])
+}
diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c
index 2ec4471e519ca..a45c52957d770 100644
--- a/clang/test/Sema/ptrauth-qualifier.c
+++ b/clang/test/Sema/ptrauth-qualifier.c
@@ -24,8 +24,8 @@ typedef int *intp;
int nonConstantGlobal = 5;
__ptrauth(INVALID_KEY) int invalid2; // expected-error{{200 does not identify a valid pointer authentication key for the current target}}
-__ptrauth(VALID_DATA_KEY) int invalid3; // expected-error {{'__ptrauth' qualifier only applies to pointer types; 'int' is invalid}}
-__ptrauth(VALID_DATA_KEY) int *invalid4; // expected-error {{'__ptrauth' qualifier only applies to pointer types; 'int' is invalid}}
+__ptrauth(VALID_DATA_KEY) int invalid3; // expected-error {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'int' is invalid}}
+__ptrauth(VALID_DATA_KEY) int *invalid4; // expected-error {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'int' is invalid}}
int * (__ptrauth(VALID_DATA_KEY) invalid5); // expected-error{{expected identifier or '('}} expected-error{{expected ')'}} expected-note {{to match this '('}}
int *__ptrauth(VALID_DATA_KEY) __ptrauth(VALID_DATA_KEY) invalid6; // expected-error{{type 'int *__ptrauth(2,0,0)' is already __ptrauth-qualified}}
int * __ptrauth(VALID_DATA_KEY, 2) invalid7; // expected-error {{invalid address discrimination flag '2'; '__ptrauth' requires '0' or '1'}}
@@ -102,7 +102,7 @@ __attribute__((overloadable)) float overload_func(int * __ptrauth(VALID_DATA_KEY
static_assert(_Generic(typeof(overload_func(&ptr0)), int : 1, default : 0));
static_assert(_Generic(typeof(overload_func(&valid0)), float : 1, default : 0));
-void func(int array[__ptrauth(VALID_DATA_KEY) 10]); // expected-error {{'__ptrauth' qualifier only applies to pointer types; 'int[10]' is invalid}}
+void func(int array[__ptrauth(VALID_DATA_KEY) 10]); // expected-error {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'int[10]' is invalid}}
struct S0 { // expected-note 4 {{struct S0' has subobjects that are non-trivial to copy}}
intp __ptrauth(1, 1, 50) f0; // expected-note 4 {{f0 has type '__ptrauth(1,1,50) intp' (aka 'int *__ptrauth(1,1,50)') that is non-trivial to copy}}
diff --git a/clang/test/SemaCXX/ptrauth-triviality.cpp b/clang/test/SemaCXX/ptrauth-triviality.cpp
index 785e83aaaa545..60d1b57230f18 100644
--- a/clang/test/SemaCXX/ptrauth-triviality.cpp
+++ b/clang/test/SemaCXX/ptrauth-triviality.cpp
@@ -3,6 +3,8 @@
#define AQ __ptrauth(1,1,50)
#define IQ __ptrauth(1,0,50)
+#define AQ_IP __ptrauth(1,1,50)
+#define IQ_IP __ptrauth(1,0,50)
#define AA [[clang::ptrauth_vtable_pointer(process_independent,address_discrimination,no_extra_discrimination)]]
#define IA [[clang::ptrauth_vtable_pointer(process_independent,no_address_discrimination,type_discrimination)]]
#define PA [[clang::ptrauth_vtable_pointer(process_dependent,no_address_discrimination,no_extra_discrimination)]]
@@ -134,3 +136,49 @@ static_assert(!__is_trivially_copyable(Holder<S5>));
static_assert(__is_trivially_relocatable(Holder<S5>)); // expected-warning{{deprecated}}
static_assert(__builtin_is_cpp_trivially_relocatable(Holder<S5>));
static_assert(!__is_trivially_equality_comparable(Holder<S5>));
+
+struct S6 {
+ __INTPTR_TYPE__ AQ_IP p_;
+ void *payload_;
+ bool operator==(const S6&) const = default;
+};
+static_assert(__is_trivially_constructible(S6));
+static_assert(!__is_trivially_constructible(S6, const S6&));
+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(!__is_trivially_equality_comparable(S6));
+
+static_assert(__is_trivially_constructible(Holder<S6>));
+static_assert(!__is_trivially_constructible(Holder<S6>, const Holder<S6>&));
+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(!__is_trivially_equality_comparable(Holder<S6>));
+
+struct S7 {
+ __INTPTR_TYPE__ IQ_IP p_;
+ void *payload_;
+ bool operator==(const S7&) const = default;
+};
+static_assert(__is_trivially_constructible(S7));
+static_assert(__is_trivially_constructible(S7, const S7&));
+static_assert(__is_trivially_assignable(S7&, const S7&));
+static_assert(__is_trivially_destructible(S7));
+static_assert(__is_trivially_copyable(S7));
+static_assert(__is_trivially_relocatable(S7)); // expected-warning{{deprecated}}
+static_assert(__builtin_is_cpp_trivially_relocatable(S7));
+static_assert(__is_trivially_equality_comparable(S7));
+
+static_assert(__is_trivially_constructible(Holder<S7>));
+static_assert(__is_trivially_constructible(Holder<S7>, const Holder<S7>&));
+static_assert(__is_trivially_assignable(Holder<S7>, const Holder<S7>&));
+static_assert(__is_trivially_destructible(Holder<S7>));
+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>));
>From 0879a5f2e98d4228310cbac9686c57ae3262b46c Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Fri, 9 May 2025 11:48:58 -0700
Subject: [PATCH 2/2] Address punctuation and layout of diagnostics
---
.../include/clang/Basic/DiagnosticSemaKinds.td | 17 +++++++++--------
clang/test/Sema/ptrauth-qualifier.c | 2 +-
.../SemaCXX/ptrauth-template-parameters.cpp | 2 +-
3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 034ef4ff57187..89b2d664d66a0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1028,21 +1028,22 @@ def err_ptrauth_indirect_goto_addrlabel_arithmetic : Error<
// __ptrauth qualifier
def err_ptrauth_qualifier_invalid : Error<
- "%select{return type|parameter type|property}1 may not be qualified with '__ptrauth'; type is %0">;
+ "%select{return type|parameter type|property}1 may not be qualified with "
+ "'__ptrauth'; type is %0">;
def err_ptrauth_qualifier_cast : Error<
"cannot cast to '__ptrauth'-qualified type %0">;
-def err_ptrauth_qualifier_invalid_target
- : Error<"'__ptrauth' qualifier only applies to pointer or pointer sized "
- "integer"
- " types; %0 is invalid">;
-def err_ptrauth_qualifier_redundant
- : Error<"type %0 is already __ptrauth-qualified">;
+def err_ptrauth_qualifier_invalid_target : Error<
+ "'__ptrauth' qualifier only applies to pointer or pointer sized integer "
+ "types; %0 is invalid">;
+def err_ptrauth_qualifier_redundant: Error<
+ "type %0 is already '__ptrauth'-qualified">;
def err_ptrauth_arg_not_ice : Error<
"argument to '__ptrauth' must be an integer constant expression">;
def err_ptrauth_address_discrimination_invalid : Error<
"invalid address discrimination flag '%0'; '__ptrauth' requires '0' or '1'">;
def err_ptrauth_extra_discriminator_invalid : Error<
- "invalid extra discriminator flag '%0'; '__ptrauth' requires a value between '0' and '%1'">;
+ "invalid extra discriminator flag '%0'; '__ptrauth' requires a value between "
+ "'0' and '%1'">;
/// main()
// static main() is not an error in C, just in C++.
diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c
index a45c52957d770..5d932b724f07a 100644
--- a/clang/test/Sema/ptrauth-qualifier.c
+++ b/clang/test/Sema/ptrauth-qualifier.c
@@ -27,7 +27,7 @@ __ptrauth(INVALID_KEY) int invalid2; // expected-error{{200 does not identify a
__ptrauth(VALID_DATA_KEY) int invalid3; // expected-error {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'int' is invalid}}
__ptrauth(VALID_DATA_KEY) int *invalid4; // expected-error {{'__ptrauth' qualifier only applies to pointer or pointer sized integer types; 'int' is invalid}}
int * (__ptrauth(VALID_DATA_KEY) invalid5); // expected-error{{expected identifier or '('}} expected-error{{expected ')'}} expected-note {{to match this '('}}
-int *__ptrauth(VALID_DATA_KEY) __ptrauth(VALID_DATA_KEY) invalid6; // expected-error{{type 'int *__ptrauth(2,0,0)' is already __ptrauth-qualified}}
+int *__ptrauth(VALID_DATA_KEY) __ptrauth(VALID_DATA_KEY) invalid6; // expected-error{{type 'int *__ptrauth(2,0,0)' is already '__ptrauth'-qualified}}
int * __ptrauth(VALID_DATA_KEY, 2) invalid7; // expected-error {{invalid address discrimination flag '2'; '__ptrauth' requires '0' or '1'}}
int * __ptrauth(VALID_DATA_KEY, -1) invalid8; // expected-error {{invalid address discrimination flag '-1'; '__ptrauth' requires '0' or '1'}}
int * __ptrauth(VALID_DATA_KEY, 1, -1) invalid9; // expected-error {{invalid extra discriminator flag '-1'; '__ptrauth' requires a value between '0' and '65535'}}
diff --git a/clang/test/SemaCXX/ptrauth-template-parameters.cpp b/clang/test/SemaCXX/ptrauth-template-parameters.cpp
index ee23d3f2ec456..99899ad11dbe5 100644
--- a/clang/test/SemaCXX/ptrauth-template-parameters.cpp
+++ b/clang/test/SemaCXX/ptrauth-template-parameters.cpp
@@ -3,7 +3,7 @@
template <typename T> struct G {
T __ptrauth(0,0,1234) test;
- // expected-error at -1 2 {{type '__ptrauth(0,0,1234) T' is already __ptrauth-qualified}}
+ // expected-error at -1 2 {{type '__ptrauth(0,0,1234) T' is already '__ptrauth'-qualified}}
};
template <typename T> struct Indirect {
More information about the cfe-commits
mailing list