[clang] [clang][PAC] add support for options parameter to __ptrauth (PR #136828)
Oliver Hunt via cfe-commits
cfe-commits at lists.llvm.org
Fri May 9 21:57:08 PDT 2025
https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/136828
>From 6f161d3699cac2000069f74376dc02697a7242b5 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Wed, 30 Apr 2025 22:26:59 -0700
Subject: [PATCH] [clang][PAC] add support for options parameter to __ptrauth
This PR adds support for an 'options' parameter for the __ptrauth
qualifier.
The initial version only exposes the authehntication modes:
* "strip"
* "sign-and-strip"
* "sign-and-auth"
We also support parsing the options but not yet the implementation
* "isa-pointer"
* "authenticates-null-values"
The initial support for authentication mode controls exist to support
ABI changes over time, and as a byproduct support basic initial tests
for option parsing.
---
clang/include/clang/Basic/Attr.td | 6 +-
.../clang/Basic/DiagnosticParseKinds.td | 4 +-
.../clang/Basic/DiagnosticSemaKinds.td | 18 +-
clang/include/clang/Basic/LangOptions.h | 11 +
clang/lib/CodeGen/CGExprConstant.cpp | 24 +-
clang/lib/Parse/ParseDecl.cpp | 2 +-
clang/lib/Sema/SemaType.cpp | 159 ++++++++-
clang/test/CodeGen/ptrauth-stripping.c | 327 ++++++++++++++++++
clang/test/Parser/ptrauth-qualifier.c | 2 +-
clang/test/Sema/ptrauth-qualifier-options.c | 65 ++++
clang/test/Sema/ptrauth-qualifier.c | 39 ++-
.../ptrauth-qualifier-constexpr-options.cpp | 65 ++++
12 files changed, 692 insertions(+), 30 deletions(-)
create mode 100644 clang/test/CodeGen/ptrauth-stripping.c
create mode 100644 clang/test/Sema/ptrauth-qualifier-options.c
create mode 100644 clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 37c80ac90182c..1941bb2d1febc 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3595,9 +3595,9 @@ def ObjCRequiresPropertyDefs : InheritableAttr {
def PointerAuth : TypeAttr {
let Spellings = [CustomKeyword<"__ptrauth">];
- let Args = [IntArgument<"Key">,
- BoolArgument<"AddressDiscriminated", 1>,
- IntArgument<"ExtraDiscriminator", 1>];
+ let Args = [IntArgument<"Key">, BoolArgument<"AddressDiscriminated", 1>,
+ IntArgument<"ExtraDiscriminator", 1>,
+ StringArgument<"Options", 1>];
let Documentation = [PtrAuthDocs];
}
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 3bbdc49946dac..8e0f818714c42 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1735,8 +1735,8 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning<
"argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">,
InGroup<CudaCompat>;
-def err_ptrauth_qualifier_bad_arg_count : Error<
- "'__ptrauth' qualifier must take between 1 and 3 arguments">;
+def err_ptrauth_qualifier_bad_arg_count
+ : Error<"'__ptrauth' qualifier must take between 1 and 4 arguments">;
def warn_cuda_attr_lambda_position : Warning<
"nvcc does not allow '__%0__' to appear after the parameter list in lambdas">,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 89b2d664d66a0..0ae2c09b1e4fb 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1045,6 +1045,23 @@ def err_ptrauth_extra_discriminator_invalid : Error<
"invalid extra discriminator flag '%0'; '__ptrauth' requires a value between "
"'0' and '%1'">;
+// __ptrauth qualifier options string
+def note_ptrauth_evaluating_options
+ : Note<"options parameter evaluated to '%0'">;
+def err_ptrauth_invalid_option : Error<"'%0' options parameter %1">;
+def err_ptrauth_unknown_authentication_option
+ : Error<"unknown '%0' authentication option '%1'">;
+def err_ptrauth_repeated_authentication_option
+ : Error<"repeated '%0' authentication %select{mode|option}1%select{, prior mode was '%3'| '%2'}1">;
+def note_ptrauth_previous_authentication_option
+ : Note<"previous '%0' authentication %select{mode|option}1">;
+def err_ptrauth_unexpected_option_end
+ : Error<"unexpected end of options parameter for %0">;
+def err_ptrauth_option_unexpected_token
+ : Error<"unexpected character '%0' in '%1' options">;
+def err_ptrauth_option_missing_comma
+ : Error<"missing comma after '%1' option in '%0' qualifier">;
+
/// main()
// static main() is not an error in C, just in C++.
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -1735,7 +1752,6 @@ def err_static_assert_requirement_failed : Error<
def note_expr_evaluates_to : Note<
"expression evaluates to '%0 %1 %2'">;
-
def subst_user_defined_msg : TextSubstitution<
"%select{the message|the expression}0 in "
"%select{a static assertion|this asm operand}0">;
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 491e8bee9fd5c..3944946374d30 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -65,6 +65,17 @@ enum class PointerAuthenticationMode : unsigned {
SignAndAuth
};
+static constexpr llvm::StringLiteral PointerAuthenticationOptionStrip = "strip";
+static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndStrip =
+ "sign-and-strip";
+static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndAuth =
+ "sign-and-auth";
+static constexpr llvm::StringLiteral PointerAuthenticationOptionIsaPointer =
+ "isa-pointer";
+static constexpr llvm::StringLiteral
+ PointerAuthenticationOptionAuthenticatesNullValues =
+ "authenticates-null-values";
+
/// Bitfields of LangOptions, split out from LangOptions in order to ensure that
/// this large collection of bitfields is a trivial class type.
class LangOptionsBase {
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 6ba45f42db4d1..1de3d8a181343 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2129,6 +2129,13 @@ class ConstantLValueEmitter : public ConstStmtVisitor<ConstantLValueEmitter,
}
+static bool shouldSignPointer(const PointerAuthQualifier &PointerAuth) {
+ PointerAuthenticationMode AuthenticationMode =
+ PointerAuth.getAuthenticationMode();
+ return AuthenticationMode == PointerAuthenticationMode::SignAndStrip ||
+ AuthenticationMode == PointerAuthenticationMode::SignAndAuth;
+}
+
llvm::Constant *ConstantLValueEmitter::tryEmit() {
const APValue::LValueBase &base = Value.getLValueBase();
@@ -2162,7 +2169,8 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() {
// Apply pointer-auth signing from the destination type.
if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth();
- PointerAuth && !result.HasDestPointerAuth) {
+ PointerAuth && !result.HasDestPointerAuth &&
+ shouldSignPointer(PointerAuth)) {
value = Emitter.tryEmitConstantSignedPointer(value, PointerAuth);
if (!value)
return nullptr;
@@ -2210,8 +2218,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
if (D->hasAttr<WeakRefAttr>())
return CGM.GetWeakRefReference(D).getPointer();
- auto PtrAuthSign = [&](llvm::Constant *C) {
- if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth()) {
+ auto PtrAuthSign = [&](llvm::Constant *C, bool IsFunction) {
+ if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth();
+ PointerAuth && shouldSignPointer(PointerAuth)) {
C = applyOffset(C);
C = Emitter.tryEmitConstantSignedPointer(C, PointerAuth);
return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true);
@@ -2219,7 +2228,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
CGPointerAuthInfo AuthInfo;
- if (EnablePtrAuthFunctionTypeDiscrimination)
+ if (IsFunction && EnablePtrAuthFunctionTypeDiscrimination)
AuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
if (AuthInfo) {
@@ -2237,17 +2246,18 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
};
if (const auto *FD = dyn_cast<FunctionDecl>(D))
- return PtrAuthSign(CGM.getRawFunctionPointer(FD));
+ return PtrAuthSign(CGM.getRawFunctionPointer(FD), /*IsFunction=*/true);
if (const auto *VD = dyn_cast<VarDecl>(D)) {
// We can never refer to a variable with local storage.
if (!VD->hasLocalStorage()) {
if (VD->isFileVarDecl() || VD->hasExternalStorage())
- return CGM.GetAddrOfGlobalVar(VD);
+ return PtrAuthSign(CGM.GetAddrOfGlobalVar(VD), /*IsFunction=*/false);
if (VD->isLocalVarDecl()) {
- return CGM.getOrCreateStaticVarDecl(
+ llvm::Constant *C = CGM.getOrCreateStaticVarDecl(
*VD, CGM.getLLVMLinkageVarDefinition(VD));
+ return PtrAuthSign(C, /*IsFunction=*/false);
}
}
}
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 4fe3565687905..259ce5029271e 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3428,7 +3428,7 @@ void Parser::ParsePtrauthQualifier(ParsedAttributes &Attrs) {
T.consumeClose();
SourceLocation EndLoc = T.getCloseLocation();
- if (ArgExprs.empty() || ArgExprs.size() > 3) {
+ if (ArgExprs.empty() || ArgExprs.size() > 4) {
Diag(KwLoc, diag::err_ptrauth_qualifier_bad_arg_count);
return;
}
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index a8e85c885069e..654ac4d903f51 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -8350,14 +8350,16 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr,
/// Handle the __ptrauth qualifier.
static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
const ParsedAttr &Attr, Sema &S) {
-
- assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 3) &&
- "__ptrauth qualifier takes between 1 and 3 arguments");
+ assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 4) &&
+ "__ptrauth qualifier takes between 1 and 4 arguments");
+ StringRef AttrName = Attr.getAttrName()->getName();
Expr *KeyArg = Attr.getArgAsExpr(0);
Expr *IsAddressDiscriminatedArg =
Attr.getNumArgs() >= 2 ? Attr.getArgAsExpr(1) : nullptr;
Expr *ExtraDiscriminatorArg =
Attr.getNumArgs() >= 3 ? Attr.getArgAsExpr(2) : nullptr;
+ Expr *AuthenticationOptionsArg =
+ Attr.getNumArgs() >= 4 ? Attr.getArgAsExpr(3) : nullptr;
unsigned Key;
if (S.checkConstantPointerAuthKey(KeyArg, Key)) {
@@ -8373,10 +8375,140 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
IsAddressDiscriminated);
IsInvalid |= !S.checkPointerAuthDiscriminatorArg(
ExtraDiscriminatorArg, PointerAuthDiscArgKind::Extra, ExtraDiscriminator);
+ std::string LastAuthenticationMode;
+ std::optional<PointerAuthenticationMode> AuthenticationMode = std::nullopt;
+ bool IsIsaPointer = false;
+ bool AuthenticatesNullValues = false;
+
+ if (AuthenticationOptionsArg && !AuthenticationOptionsArg->containsErrors()) {
+ StringRef OptionsString;
+ std::string EvaluatedString;
+ bool HasEvaluatedOptionsString = false;
+ const StringLiteral *OptionsStringLiteral =
+ dyn_cast<StringLiteral>(AuthenticationOptionsArg);
+ SourceRange AuthenticationOptionsRange =
+ AuthenticationOptionsArg->getSourceRange();
+ bool ReportedEvaluation = false;
+ auto ReportEvaluationOfExpressionIfNeeded = [&]() {
+ if (OptionsStringLiteral || !HasEvaluatedOptionsString ||
+ ReportedEvaluation)
+ return;
+ ReportedEvaluation = true;
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::note_ptrauth_evaluating_options)
+ << OptionsString << AuthenticationOptionsRange;
+ };
+ auto DiagnoseInvalidOptionsParameter = [&](llvm::StringRef Reason) {
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::err_ptrauth_invalid_option)
+ << AttrName << Reason;
+ Attr.setInvalid();
+ IsInvalid = true;
+ ReportEvaluationOfExpressionIfNeeded();
+ };
+ if (AuthenticationOptionsArg->isValueDependent() ||
+ AuthenticationOptionsArg->isTypeDependent()) {
+ DiagnoseInvalidOptionsParameter("is dependent");
+ return;
+ }
+ if (OptionsStringLiteral) {
+ OptionsString = OptionsStringLiteral->getString();
+ HasEvaluatedOptionsString = true;
+ } else {
+ Expr::EvalResult Eval;
+ bool Result = AuthenticationOptionsArg->EvaluateAsRValue(Eval, Ctx);
+ if (Result && Eval.Val.isLValue()) {
+ auto *BaseExpr = Eval.Val.getLValueBase().dyn_cast<const Expr *>();
+ const StringLiteral *EvaluatedStringLiteral =
+ dyn_cast<StringLiteral>(const_cast<Expr *>(BaseExpr));
+ if (EvaluatedStringLiteral) {
+ CharUnits StartOffset = Eval.Val.getLValueOffset();
+ EvaluatedString = EvaluatedStringLiteral->getString().drop_front(
+ StartOffset.getQuantity());
+ OptionsString = EvaluatedString;
+ HasEvaluatedOptionsString = true;
+ }
+ }
+ }
+ if (!HasEvaluatedOptionsString) {
+ DiagnoseInvalidOptionsParameter(
+ "must be a string of comma separated flags");
+ return;
+ }
+ for (char Ch : OptionsString) {
+ if (Ch != '-' && Ch != ',' && !isWhitespace(Ch) && !isalpha(Ch)) {
+ DiagnoseInvalidOptionsParameter("contains invalid characters");
+ return;
+ }
+ }
+ HasEvaluatedOptionsString = true;
+ OptionsString = OptionsString.trim();
+ llvm::SmallVector<StringRef> Options;
+ if (!OptionsString.empty())
+ OptionsString.split(Options, ',');
+
+ auto OptionHandler = [&](auto Value, auto *Option,
+ std::string *LastOption = nullptr) {
+ return [&, Value, Option, LastOption](StringRef OptionString) {
+ if (!*Option) {
+ *Option = Value;
+ if (LastOption)
+ *LastOption = OptionString;
+ return true;
+ }
+ bool IsAuthenticationMode =
+ std::is_same_v<decltype(Value), PointerAuthenticationMode>;
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::err_ptrauth_repeated_authentication_option)
+ << AttrName << !IsAuthenticationMode << OptionString
+ << (LastOption ? *LastOption : "");
+ return false;
+ };
+ };
- if (IsInvalid) {
- Attr.setInvalid();
- return;
+ for (unsigned Idx = 0; Idx < Options.size(); ++Idx) {
+ StringRef Option = Options[Idx].trim();
+ if (Option.empty()) {
+ bool IsLastOption = Idx == (Options.size() - 1);
+ DiagnoseInvalidOptionsParameter(
+ IsLastOption ? "has a trailing comma" : "contains an empty option");
+ continue;
+ }
+ auto SelectedHandler =
+ llvm::StringSwitch<std::function<bool(StringRef)>>(Option)
+ .Case(PointerAuthenticationOptionStrip,
+ OptionHandler(PointerAuthenticationMode::Strip,
+ &AuthenticationMode, &LastAuthenticationMode))
+ .Case(PointerAuthenticationOptionSignAndStrip,
+ OptionHandler(PointerAuthenticationMode::SignAndStrip,
+ &AuthenticationMode, &LastAuthenticationMode))
+ .Case(PointerAuthenticationOptionSignAndAuth,
+ OptionHandler(PointerAuthenticationMode::SignAndAuth,
+ &AuthenticationMode, &LastAuthenticationMode))
+ .Case(PointerAuthenticationOptionIsaPointer,
+ OptionHandler(true,
+ &IsIsaPointer))
+ .Case(PointerAuthenticationOptionAuthenticatesNullValues,
+ OptionHandler(true,
+ &AuthenticatesNullValues))
+ .Default([&](StringRef Option) {
+ if (size_t WhitespaceIndex =
+ Option.find_first_of(" \t\n\v\f\r");
+ WhitespaceIndex != Option.npos) {
+ StringRef LeadingOption = Option.slice(0, WhitespaceIndex);
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::err_ptrauth_option_missing_comma)
+ << AttrName << LeadingOption;
+ } else {
+ S.Diag(AuthenticationOptionsRange.getBegin(),
+ diag::err_ptrauth_unknown_authentication_option)
+ << AttrName << Option;
+ }
+ return false;
+ });
+ if (!SelectedHandler(Option))
+ IsInvalid = true;
+ }
}
if (!T->isSignableType(Ctx) && !T->isDependentType()) {
@@ -8385,6 +8517,9 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
return;
}
+ if (!AuthenticationMode)
+ AuthenticationMode = PointerAuthenticationMode::SignAndAuth;
+
if (T.getPointerAuth()) {
S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_redundant) << T;
Attr.setInvalid();
@@ -8397,13 +8532,19 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
return;
}
+ if (IsInvalid) {
+ Attr.setInvalid();
+ return;
+ }
+
assert((!IsAddressDiscriminatedArg || IsAddressDiscriminated <= 1) &&
"address discriminator arg should be either 0 or 1");
PointerAuthQualifier Qual = PointerAuthQualifier::Create(
- Key, IsAddressDiscriminated, ExtraDiscriminator,
- PointerAuthenticationMode::SignAndAuth, /*IsIsaPointer=*/false,
- /*AuthenticatesNullValues=*/false);
+ Key, IsAddressDiscriminated, ExtraDiscriminator, *AuthenticationMode,
+ IsIsaPointer, AuthenticatesNullValues);
+ assert(Qual.getAuthenticationMode() == *AuthenticationMode);
T = S.Context.getPointerAuthType(T, Qual);
+ assert(T.getPointerAuth().getAuthenticationMode() == *AuthenticationMode);
}
/// HandleArmSveVectorBitsTypeAttr - The "arm_sve_vector_bits" attribute is
diff --git a/clang/test/CodeGen/ptrauth-stripping.c b/clang/test/CodeGen/ptrauth-stripping.c
new file mode 100644
index 0000000000000..4e187d8debdc7
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-stripping.c
@@ -0,0 +1,327 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s -o - | FileCheck %s
+
+typedef void *NonePointer;
+typedef void *__ptrauth(1, 1, 101, "strip") StripPointer;
+typedef void *__ptrauth(1, 1, 102, "sign-and-strip") SignAndStripPointer;
+typedef void *__ptrauth(1, 1, 103, "sign-and-auth") SignAndAuthPointer;
+typedef __UINT64_TYPE__ NoneIntptr;
+typedef __UINT64_TYPE__ __ptrauth(1, 0, 105, "strip") StripIntptr;
+typedef __UINT64_TYPE__ __ptrauth(1, 0, 106, "sign-and-strip") SignAndStripIntptr;
+typedef __UINT64_TYPE__ __ptrauth(1, 0, 107, "sign-and-auth") SignAndAuthIntptr;
+
+NonePointer globalNonePointer = "foo0";
+StripPointer globalStripPointer = "foo1";
+SignAndStripPointer globalSignAndStripPointer = "foo2";
+SignAndAuthPointer globalSignAndAuthPointer = "foo3";
+NoneIntptr globalNoneIntptr = (__UINT64_TYPE__)&globalNonePointer;
+StripIntptr globalStripIntptr = (__UINT64_TYPE__)&globalStripPointer;
+SignAndStripIntptr globalSignAndStripIntptr = (__UINT64_TYPE__)&globalSignAndStripPointer;
+SignAndAuthIntptr globalSignAndAuthIntptr = (__UINT64_TYPE__)&globalSignAndAuthPointer;
+
+// CHECK: @.str = private unnamed_addr constant [5 x i8] c"foo0\00", align 1
+// CHECK: @globalNonePointer = global ptr @.str, align 8
+// CHECK: @.str.1 = private unnamed_addr constant [5 x i8] c"foo1\00", align 1
+// CHECK: @globalStripPointer = global ptr @.str.1, align 8
+// CHECK: @.str.2 = private unnamed_addr constant [5 x i8] c"foo2\00", align 1
+// CHECK: @globalSignAndStripPointer = global ptr ptrauth (ptr @.str.2, i32 1, i64 102, ptr @globalSignAndStripPointer), align 8
+// CHECK: @.str.3 = private unnamed_addr constant [5 x i8] c"foo3\00", align 1
+// CHECK: @globalSignAndAuthPointer = global ptr ptrauth (ptr @.str.3, i32 1, i64 103, ptr @globalSignAndAuthPointer), align 8
+// CHECK: @globalNoneIntptr = global i64 ptrtoint (ptr @globalNonePointer to i64), align 8
+// CHECK: @globalStripIntptr = global i64 ptrtoint (ptr @globalStripPointer to i64), align 8
+// CHECK: @globalSignAndStripIntptr = global i64 ptrtoint (ptr ptrauth (ptr @globalSignAndStripPointer, i32 1, i64 106) to i64), align 8
+// CHECK: @globalSignAndAuthIntptr = global i64 ptrtoint (ptr ptrauth (ptr @globalSignAndAuthPointer, i32 1, i64 107) to i64), align 8
+
+typedef struct {
+ NonePointer ptr;
+ NoneIntptr i;
+} NoneStruct;
+typedef struct {
+ StripPointer ptr;
+ StripIntptr i;
+} StripStruct;
+typedef struct {
+ SignAndStripPointer ptr;
+ SignAndStripIntptr i;
+} SignAndStripStruct;
+typedef struct {
+ SignAndAuthPointer ptr;
+ SignAndAuthIntptr i;
+} SignAndAuthStruct;
+
+// CHECK-LABEL: @testNone
+NoneStruct testNone(NoneStruct *a, NoneStruct *b, NoneStruct c) {
+ globalNonePointer += 1;
+ // CHECK: [[GLOBALP:%.*]] = load ptr, ptr @globalNonePointer
+ // CHECK: [[GLOBALPP:%.*]] = getelementptr inbounds i8, ptr [[GLOBALP]], i64 1
+ // CHECK: store ptr [[GLOBALPP]], ptr @globalNonePointer
+ globalNoneIntptr += 1;
+ // CHECK: [[GLOBALI:%.*]] = load i64, ptr @globalNoneIntptr
+ // CHECK: [[GLOBALIP:%.*]] = add i64 [[GLOBALI]], 1
+ // CHECK: store i64 [[GLOBALIP]], ptr @globalNoneIntptr
+ a->ptr += 1;
+ // CHECK: [[PTR:%.*]] = load ptr, ptr %a.addr, align 8
+ // CHECK: [[PTR_PTR:%.*]] = getelementptr inbounds nuw %struct.NoneStruct, ptr [[PTR]], i32 0, i32 0
+ // CHECK: [[PTR:%.*]] = load ptr, ptr [[PTR_PTR]], align 8
+ // CHECK: [[AP:%.*]] = getelementptr inbounds i8, ptr [[PTR]], i64 1
+ // CHECK: store ptr [[AP]], ptr [[PTR_PTR]], align 8
+ a->i += 1;
+ // CHECK: [[PTR:%.*]] = load ptr, ptr %a.addr, align 8
+ // CHECK: [[I_PTR:%.*]] = getelementptr inbounds nuw %struct.NoneStruct, ptr [[PTR]], i32 0, i32 1
+ // CHECK: [[I:%.*]] = load i64, ptr [[I_PTR]], align 8
+ // CHECK: [[IP:%.*]] = add i64 [[I]], 1
+ // CHECK: store i64 [[IP]], ptr [[I_PTR]], align 8
+ *b = *a;
+ // CHECK: [[B_ADDR:%.*]] = load ptr, ptr %b.addr, align 8
+ // CHECK: [[A_ADDR:%.*]] = load ptr, ptr %a.addr, align 8
+ // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[B_ADDR]], ptr align 8 [[A_ADDR]], i64 16, i1 false)
+ return c;
+}
+
+// CHECK-LABEL: @testStrip1
+void testStrip1() {
+ globalStripPointer += 1;
+ // CHECK: [[GLOBALP:%.*]] = load ptr, ptr @globalStripPointer
+ // CHECK: [[GLOBALPI:%.*]] = ptrtoint ptr [[GLOBALP]] to i64
+ // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[GLOBALPI]], i32 1)
+}
+// CHECK-LABEL: @testStrip2
+void testStrip2(StripStruct *a) {
+ a->ptr += 1;
+ // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+ // CHECK: [[PTR:%.*]] = getelementptr inbounds nuw %struct.StripStruct, ptr [[A]], i32 0, i32 0
+ // CHECK: [[APTR:%.*]] = load ptr, ptr [[PTR]]
+ // CHECK: [[APTRI:%.*]] = ptrtoint ptr [[APTR]] to i64
+ // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[APTRI]], i32 1)
+}
+// CHECK-LABEL: @testStrip3
+void testStrip3(StripStruct *a) {
+ a->i += 1;
+ // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+ // CHECK: [[I:%.*]] = getelementptr inbounds nuw %struct.StripStruct, ptr [[A]], i32 0, i32 1
+ // CHECK: [[I64:%.*]] = load i64, ptr [[I]]
+ // CHECK: {{%.*}} = call i64 @llvm.ptrauth.strip(i64 [[I64]], i32 1)
+}
+// CHECK-LABEL: @testStrip4
+void testStrip4(StripStruct *a, StripStruct *b) {
+ *b = *a;
+ // CHECK: call void @__copy_assignment_8_8_pa1_101_0_t8w8(ptr %0, ptr %1)
+}
+
+// CHECK-LABEL: @testStrip5
+StripStruct testStrip5(StripStruct a) {
+ return a;
+ // CHECK: call void @__copy_constructor_8_8_pa1_101_0_t8w8(ptr %agg.result, ptr %a)
+}
+
+// CHECK-LABEL: @testSignAndStrip1
+void testSignAndStrip1(void) {
+ globalSignAndStripPointer += 1;
+ // CHECK: [[GP:%.*]] = load ptr, ptr @globalSignAndStripPointer
+ // CHECK: [[GPI:%.*]] = ptrtoint ptr [[GP]] to i64
+ // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[GPI]], i32 1)
+ // CHECK: [[STRIPPEDP:%.*]] = inttoptr i64 [[STRIPPED]] to ptr
+ // CHECK: [[PHI:%.*]] = phi ptr [ null, %entry ], [ [[STRIPPEDP]], %resign.nonnull ]
+ // CHECK: [[ADDPTR:%.*]] = getelementptr inbounds i8, ptr [[PHI]], i64 1
+ // CHECK: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @globalSignAndStripPointer to i64), i64 102)
+ // CHECK: [[ADDPTRI:%.*]] = ptrtoint ptr [[ADDPTR]] to i64
+ // CHECK: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 [[ADDPTRI]], i32 1, i64 [[DISC]])
+}
+
+// CHECK-LABEL: @testSignAndStrip2
+void testSignAndStrip2(SignAndStripStruct *a) {
+ a->ptr += 1;
+ // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+ // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr [[A]], i32 0, i32 0
+ // CHECK: [[APTR:%.*]] = load ptr, ptr %ptr
+ // CHECK: [[APTRI:%.*]] = ptrtoint ptr [[APTR]] to i64
+ // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[APTRI]], i32 1)
+ // CHECK: [[STRIPPEDP:%.*]] = inttoptr i64 [[STRIPPED]] to ptr
+ // CHECK: [[PHI:%.*]] = phi ptr [ null, %entry ], [ [[STRIPPEDP]], %resign.nonnull ]
+ // CHECK: %add.ptr = getelementptr inbounds i8, ptr [[PHI]], i64 1
+ // CHECK: [[PTRI:%.*]] = ptrtoint ptr %ptr to i64
+ // CHECK: [[DISC:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[PTRI]], i64 102)
+ // CHECK: [[APTRI:%.*]] = ptrtoint ptr %add.ptr to i64
+ // CHECK: call i64 @llvm.ptrauth.sign(i64 [[APTRI]], i32 1, i64 [[DISC]])
+}
+
+// CHECK-LABEL: @testSignAndStrip3
+void testSignAndStrip3(SignAndStripStruct *a) {
+ a->i += 1;
+ // CHECK: [[A:%.*]] = load ptr, ptr %a.addr
+ // CHECK: [[I:%.*]] = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr [[A]], i32 0, i32 1
+ // CHECK: [[I64:%.*]] = load i64, ptr [[I]]
+ // CHECK: [[STRIPPED:%.*]] = call i64 @llvm.ptrauth.strip(i64 [[I64]], i32 1)
+ // CHECK: [[PHI:%.*]] = phi i64 [ 0, %entry ], [ [[STRIPPED]], %resign.nonnull ]
+ // CHECK: %add = add i64 [[PHI]], 1
+ // CHECK: {{%.*}} = call i64 @llvm.ptrauth.sign(i64 %add, i32 1, i64 106)
+}
+
+// CHECK-LABEL: @testSignAndStrip4
+void testSignAndStrip4(SignAndStripStruct *a, SignAndStripStruct *b) {
+ *b = *a;
+ // CHECK: call void @__copy_assignment_8_8_pa1_102_0_t8w8(ptr %0, ptr %1)
+}
+
+// CHECK-LABEL: @testSignAndStrip5
+SignAndStripStruct testSignAndStrip5(SignAndStripStruct a) {
+ return a;
+ // CHECK: call void @__copy_constructor_8_8_pa1_102_0_t8w8(ptr %agg.result, ptr %a)
+}
+
+// CHECK-LABEL: @testSignAndAuth1
+void testSignAndAuth1() {
+ globalSignAndAuthPointer += 1;
+ // CHECK: %0 = load ptr, ptr @globalSignAndAuthPointer
+ // CHECK: %1 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @globalSignAndAuthPointer to i64), i64 103)
+ // CHECK: %3 = ptrtoint ptr %0 to i64
+ // CHECK: %4 = call i64 @llvm.ptrauth.auth(i64 %3, i32 1, i64 %1)
+ // CHECK: %5 = inttoptr i64 %4 to ptr
+ // CHECK: %6 = phi ptr [ null, %entry ], [ %5, %resign.nonnull ]
+ // CHECK: %add.ptr = getelementptr inbounds i8, ptr %6, i64 1
+ // CHECK: %7 = call i64 @llvm.ptrauth.blend(i64 ptrtoint (ptr @globalSignAndAuthPointer to i64), i64 103)
+ // CHECK: %8 = ptrtoint ptr %add.ptr to i64
+ // CHECK: %9 = call i64 @llvm.ptrauth.sign(i64 %8, i32 1, i64 %7)
+}
+
+// CHECK-LABEL: @testSignAndAuth2
+void testSignAndAuth2(SignAndAuthStruct *a) {
+ a->i += 1;
+ // CHECK: %0 = load ptr, ptr %a.addr
+ // CHECK: %i = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, i32 0, i32 1
+ // CHECK: %1 = load i64, ptr %i
+ // CHECK: %3 = call i64 @llvm.ptrauth.auth(i64 %1, i32 1, i64 107)
+ // CHECK: %4 = phi i64 [ 0, %entry ], [ %3, %resign.nonnull ]
+ // CHECK: %add = add i64 %4, 1
+ // CHECK: %6 = call i64 @llvm.ptrauth.sign(i64 %add, i32 1, i64 107)
+ // CHECK: %7 = phi i64 [ 0, %resign.cont ], [ %6, %resign.nonnull1 ]
+}
+
+// CHECK-LABEL: @testSignAndAuth3
+void testSignAndAuth3(SignAndAuthStruct *a) {
+ a->ptr += 1;
+ // CHECK: %0 = load ptr, ptr %a.addr
+ // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, i32 0, i32 0
+ // CHECK: %1 = load ptr, ptr %ptr
+ // CHECK: %2 = ptrtoint ptr %ptr to i64
+ // CHECK: %3 = call i64 @llvm.ptrauth.blend(i64 %2, i64 103)
+ // CHECK: %5 = ptrtoint ptr %1 to i64
+ // CHECK: %6 = call i64 @llvm.ptrauth.auth(i64 %5, i32 1, i64 %3)
+ // CHECK: %7 = inttoptr i64 %6 to ptr
+ // CHECK: %8 = phi ptr [ null, %entry ], [ %7, %resign.nonnull ]
+ // CHECK: %add.ptr = getelementptr inbounds i8, ptr %8, i64 1
+ // CHECK: %9 = ptrtoint ptr %ptr to i64
+ // CHECK: %10 = call i64 @llvm.ptrauth.blend(i64 %9, i64 103)
+ // CHECK: %11 = ptrtoint ptr %add.ptr to i64
+ // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %10)
+}
+
+// CHECK-LABEL: @testSignAndAuth4
+void testSignAndAuth4(SignAndAuthStruct *a, SignAndAuthStruct *b) {
+ *b = *a;
+ // CHECK: call void @__copy_assignment_8_8_pa1_103_0_t8w8(ptr %0, ptr %1)
+}
+
+// CHECK-LABEL: @testSignAndAuth5
+SignAndAuthStruct testSignAndAuth5(SignAndAuthStruct a) {
+ return a;
+ // CHECK: call void @__copy_constructor_8_8_pa1_103_0_t8w8(ptr %agg.result, ptr %a)
+}
+
+// CHECK-LABEL: @testCoercions1
+void testCoercions1(StripStruct *a, SignAndStripStruct *b) {
+ a->ptr = b->ptr;
+ // CHECK: %0 = load ptr, ptr %a.addr
+ // CHECK: %ptr = getelementptr inbounds nuw %struct.StripStruct, ptr %0, i32 0, i32 0
+ // CHECK: %1 = load ptr, ptr %b.addr
+ // CHECK: %ptr1 = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr %1, i32 0, i32 0
+ // CHECK: %2 = load ptr, ptr %ptr1
+ // CHECK: %3 = ptrtoint ptr %ptr1 to i64
+ // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 102)
+ // CHECK: %8 = ptrtoint ptr %2 to i64
+ // CHECK: %9 = call i64 @llvm.ptrauth.strip(i64 %8, i32 1)
+}
+
+// CHECK-LABEL: @testCoercions2
+void testCoercions2(StripStruct *a, SignAndAuthStruct *b) {
+ b->ptr = a->ptr;
+ // CHECK: store ptr %a, ptr %a.addr
+ // CHECK: store ptr %b, ptr %b.addr
+ // CHECK: %0 = load ptr, ptr %b.addr
+ // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, i32 0, i32 0
+ // CHECK: %1 = load ptr, ptr %a.addr
+ // CHECK: %ptr1 = getelementptr inbounds nuw %struct.StripStruct, ptr %1, i32 0, i32 0
+ // CHECK: %2 = load ptr, ptr %ptr1
+ // CHECK: %3 = ptrtoint ptr %ptr1 to i64
+ // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 101)
+ // CHECK: %5 = ptrtoint ptr %ptr to i64
+ // CHECK: %6 = call i64 @llvm.ptrauth.blend(i64 %5, i64 103)
+ // CHECK: %7 = icmp ne ptr %2, null
+ // CHECK: %8 = ptrtoint ptr %2 to i64
+ // CHECK: %9 = call i64 @llvm.ptrauth.strip(i64 %8, i32 1)
+ // CHECK: %10 = inttoptr i64 %9 to ptr
+ // CHECK: %11 = ptrtoint ptr %10 to i64
+ // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %6)
+}
+
+// CHECK-LABEL: @testCoercions3
+void testCoercions3(SignAndStripStruct *a, SignAndAuthStruct *b) {
+ a->ptr = b->ptr;
+ // CHECK: %0 = load ptr, ptr %a.addr
+ // CHECK: %ptr = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr %0, i32 0, i32 0
+ // CHECK: %1 = load ptr, ptr %b.addr
+ // CHECK: %ptr1 = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %1, i32 0, i32 0
+ // CHECK: %2 = load ptr, ptr %ptr1
+ // CHECK: %3 = ptrtoint ptr %ptr1 to i64
+ // CHECK: %4 = call i64 @llvm.ptrauth.blend(i64 %3, i64 103)
+ // CHECK: %5 = ptrtoint ptr %ptr to i64
+ // CHECK: %6 = call i64 @llvm.ptrauth.blend(i64 %5, i64 102)
+ // CHECK: %8 = ptrtoint ptr %2 to i64
+ // CHECK: %9 = call i64 @llvm.ptrauth.auth(i64 %8, i32 1, i64 %4)
+ // CHECK: %10 = inttoptr i64 %9 to ptr
+ // CHECK: %11 = ptrtoint ptr %10 to i64
+ // CHECK: %12 = call i64 @llvm.ptrauth.sign(i64 %11, i32 1, i64 %6)
+ // CHECK: %13 = inttoptr i64 %12 to ptr
+ // CHECK: %14 = phi ptr [ null, %entry ], [ %13, %resign.nonnull ]
+}
+
+// CHECK-LABEL: @testCoercions4
+void testCoercions4(StripStruct *a, SignAndStripStruct *b) {
+ a->i = b->i;
+ // CHECK: store ptr %a, ptr %a.addr
+ // CHECK: store ptr %b, ptr %b.addr
+ // CHECK: %0 = load ptr, ptr %a.addr
+ // CHECK: %i = getelementptr inbounds nuw %struct.StripStruct, ptr %0, i32 0, i32 1
+ // CHECK: %1 = load ptr, ptr %b.addr
+ // CHECK: %i1 = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr %1, i32 0, i32 1
+ // CHECK: %2 = load i64, ptr %i1
+ // CHECK: %4 = call i64 @llvm.ptrauth.strip(i64 %2, i32 1)
+ // CHECK: %5 = phi i64 [ 0, %entry ], [ %4, %resign.nonnull ]
+}
+
+// CHECK-LABEL: @testCoercions5
+void testCoercions5(StripStruct *a, SignAndAuthStruct *b) {
+ b->i = a->i;
+ // CHECK: %0 = load ptr, ptr %b.addr
+ // CHECK: %i = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %0, i32 0, i32 1
+ // CHECK: %1 = load ptr, ptr %a.addr
+ // CHECK: %i1 = getelementptr inbounds nuw %struct.StripStruct, ptr %1, i32 0, i32 1
+ // CHECK: %2 = load i64, ptr %i1
+ // CHECK: %4 = call i64 @llvm.ptrauth.strip(i64 %2, i32 1)
+ // CHECK: %5 = call i64 @llvm.ptrauth.sign(i64 %4, i32 1, i64 107)
+ // CHECK: %6 = phi i64 [ 0, %entry ], [ %5, %resign.nonnull ]
+ // CHECK: store i64 %6, ptr %i
+}
+
+// CHECK-LABEL: @testCoercions6
+void testCoercions6(SignAndStripStruct *a, SignAndAuthStruct *b) {
+ a->i = b->i;
+ // CHECK: %0 = load ptr, ptr %a.addr
+ // CHECK: %i = getelementptr inbounds nuw %struct.SignAndStripStruct, ptr %0, i32 0, i32 1
+ // CHECK: %1 = load ptr, ptr %b.addr
+ // CHECK: %i1 = getelementptr inbounds nuw %struct.SignAndAuthStruct, ptr %1, i32 0, i32 1
+ // CHECK: %2 = load i64, ptr %i1
+ // CHECK: %3 = icmp ne i64 %2, 0
+ // CHECK: %4 = call i64 @llvm.ptrauth.auth(i64 %2, i32 1, i64 107)
+ // CHECK: %5 = call i64 @llvm.ptrauth.sign(i64 %4, i32 1, i64 106)
+ // CHECK: %6 = phi i64 [ 0, %entry ], [ %5, %resign.nonnull ]
+}
diff --git a/clang/test/Parser/ptrauth-qualifier.c b/clang/test/Parser/ptrauth-qualifier.c
index 2071ac6c2d661..014613e0a3bbd 100644
--- a/clang/test/Parser/ptrauth-qualifier.c
+++ b/clang/test/Parser/ptrauth-qualifier.c
@@ -15,4 +15,4 @@ int nonConstantGlobal = 5;
__ptrauth int invalid0; // expected-error{{expected '('}}
__ptrauth() int invalid1; // expected-error{{expected expression}}
-int * __ptrauth(VALID_DATA_KEY, 1, 1000, 12) invalid12; // expected-error{{qualifier must take between 1 and 3 arguments}}
+int * __ptrauth(VALID_DATA_KEY, 1, 1000, 12, 24) invalid12; // expected-error{{qualifier must take between 1 and 4 arguments}}
diff --git a/clang/test/Sema/ptrauth-qualifier-options.c b/clang/test/Sema/ptrauth-qualifier-options.c
new file mode 100644
index 0000000000000..b079b264ce16a
--- /dev/null
+++ b/clang/test/Sema/ptrauth-qualifier-options.c
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -std=c23 -fsyntax-only -verify -fptrauth-intrinsics %s
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c23 -fsyntax-only -verify -fptrauth-intrinsics %s
+
+_Static_assert(__has_extension(ptrauth_qualifier), "the ptrauth qualifier should be available");
+
+#if __aarch64__
+#define VALID_CODE_KEY 0
+#define VALID_DATA_KEY 2
+#define INVALID_KEY 200
+#else
+#error Provide these constants if you port this test
+#endif
+
+
+typedef int *intp;
+
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "Foo") invalid13_1;
+// expected-error at -1 {{unknown '__ptrauth' authentication option 'Foo'}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip", 41) invalid14_1;
+// expected-error at -1 {{'__ptrauth' qualifier must take between 1 and 4 arguments}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,sign-and-strip") invalid15;
+// expected-error at -1 {{repeated '__ptrauth' authentication mode, prior mode was 'strip'}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer,isa-pointer") invalid16;
+// expected-error at -1 {{repeated '__ptrauth' authentication option}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip, , isa-pointer") invalid18;
+// expected-error at -1 {{'__ptrauth' options parameter contains an empty option}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19;
+// expected-error at -1 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20;
+// expected-error at -1 {{'__ptrauth' options parameter contains an empty option}}
+// expected-error at -2 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21;
+// expected-error at -1 2 {{'__ptrauth' options parameter contains an empty option}}
+// expected-error at -2 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22;
+// expected-error at -1 {{missing comma after 'strip' option in '__ptrauth' qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23;
+// expected-error at -1 {{missing comma after 'strip' option in '__ptrauth' qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip"
+ " isa-pointer") invalid24;
+// expected-error at -2{{missing comma after 'strip' option in '__ptrauth' qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\n,isa-pointer") invalid25; // expected-error{{'__ptrauth' options parameter contains an empty option}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") invalid26; // expected-error{{'__ptrauth' options parameter contains an empty option}}
+
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip") valid12;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip") valid13;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth") valid14;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "isa-pointer") valid15;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth,isa-pointer") valid15;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,isa-pointer") valid16;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip,isa-pointer") valid17;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip,isa-pointer") valid18;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ,isa-pointer") valid19;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip, isa-pointer") valid20;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip,isa-pointer ") valid21;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip") valid22;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ") valid23;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip ") valid24;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,"
+ "isa-pointer") valid25;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip"
+ ",isa-pointer") valid26;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip\n,isa-pointer") valid27;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip\t,isa-pointer") valid28;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "") valid29;
diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c
index 5d932b724f07a..54f9334b212dc 100644
--- a/clang/test/Sema/ptrauth-qualifier.c
+++ b/clang/test/Sema/ptrauth-qualifier.c
@@ -1,6 +1,8 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -std=c23 -fsyntax-only -verify -fptrauth-intrinsics %s
// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c23 -fsyntax-only -verify -fptrauth-intrinsics %s
+#include <ptrauth.h>
+
#if !__has_extension(ptrauth_qualifier)
// This error means that the __ptrauth qualifier availability test says that it
// is not available. This error is not expected in the output, if it is seen
@@ -34,7 +36,25 @@ int * __ptrauth(VALID_DATA_KEY, 1, -1) invalid9; // expected-error {{invalid ext
int * __ptrauth(VALID_DATA_KEY, 1, 100000) invalid10; // expected-error {{invalid extra discriminator flag '100000'; '__ptrauth' requires a value between '0' and '65535'}}
int * __ptrauth(VALID_DATA_KEY, 1, nonConstantGlobal) invalid12; // expected-error {{argument to '__ptrauth' must be an integer constant expression}}
int * __ptrauth(VALID_DATA_KEY, nonConstantGlobal, 1000) invalid13; // expected-error {{argument to '__ptrauth' must be an integer constant expression}}
-int * __ptrauth(nonConstantGlobal, 1, 1000) invalid14; // expected-error{{expression is not an integer constant expression}}
+int * __ptrauth(nonConstantGlobal, 1, 1000) invalid14; // expected-error {{expression is not an integer constant expression}}
+
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, 41) invalid11; // expected-error {{'__ptrauth' options parameter must be a string of comma separated flags}}
+int *__ptrauth(VALID_DATA_KEY, 1, nonConstantGlobal) invalid12; // expected-error {{argument to '__ptrauth' must be an integer constant expression}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "Foo") invalid13; // expected-error {{unknown '__ptrauth' authentication option 'Foo'}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip", 41) invalid14; // expected-error {{'__ptrauth' qualifier must take between 1 and 4 arguments}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,sign-and-strip") invalid15; // expected-error {{repeated '__ptrauth' authentication mode, prior mode was 'strip'}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19; // expected-error {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20; // expected-error {{'__ptrauth' options parameter contains an empty option}}
+ // expected-error at -1 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21; // expected-error {{'__ptrauth' options parameter contains an empty option}}
+ // expected-error at -1 {{'__ptrauth' options parameter contains an empty option}}
+ // expected-error at -2 {{'__ptrauth' options parameter has a trailing comma}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22; // expected-error {{missing comma after 'strip' option in '__ptrauth' qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23; // expected-error {{missing comma after 'strip' option in '__ptrauth' qualifier}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip" // expected-error {{missing comma after 'strip' option in '__ptrauth' qualifier}}
+ " isa-pointer") invalid24;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\n,isa-pointer") invalid25; // expected-error {{'__ptrauth' options parameter contains an empty option}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") invalid26; // expected-error {{'__ptrauth' options parameter contains an empty option}}
int * __ptrauth(VALID_DATA_KEY) valid0;
int * __ptrauth(VALID_DATA_KEY) *valid1;
@@ -42,11 +62,18 @@ __ptrauth(VALID_DATA_KEY) intp valid2;
__ptrauth(VALID_DATA_KEY) intp *valid3;
intp __ptrauth(VALID_DATA_KEY) valid4;
intp __ptrauth(VALID_DATA_KEY) *valid5;
-int * __ptrauth(VALID_DATA_KEY, 0) valid6;
-int * __ptrauth(VALID_DATA_KEY, 1) valid7;
-int * __ptrauth(VALID_DATA_KEY, (_Bool) 1) valid8;
-int * __ptrauth(VALID_DATA_KEY, 1, 0) valid9;
-int * __ptrauth(VALID_DATA_KEY, 1, 65535) valid10;
+int *__ptrauth(VALID_DATA_KEY, 0) valid6;
+int *__ptrauth(VALID_DATA_KEY, 1) valid7;
+int *__ptrauth(VALID_DATA_KEY, (_Bool) 1) valid8;
+int *__ptrauth(VALID_DATA_KEY, 1, 0) valid9;
+int *__ptrauth(VALID_DATA_KEY, 1, 65535) valid10;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip") valid12;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip") valid13;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-auth") valid14;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip") valid22;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "strip ") valid23;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, " strip ") valid24;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "") valid29;
int * __ptrauth(VALID_DATA_KEY) array0[10];
int (* __ptrauth(VALID_DATA_KEY) array1)[10];
diff --git a/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp
new file mode 100644
index 0000000000000..941cf1c57e1ba
--- /dev/null
+++ b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++2b -Wno-string-plus-int -fptrauth-calls -fptrauth-intrinsics -verify -fsyntax-only %s
+
+struct S {
+ static constexpr auto options = "strip";
+};
+
+constexpr const char* const_options(int i) {
+ const char* local_const = "isa-pointer";
+ constexpr auto local_constexpr = ",";
+ static constexpr auto static_const = "strip";
+ static constexpr auto static_constexpr = "sign-and-strip";
+
+ switch (i) {
+ case 0:
+ return "";
+ case 1:
+ return "authenticates-null-values";
+ case 2:
+ return local_const;
+ case 3:
+ return local_constexpr;
+ case 4:
+ return static_const;
+ case 5:
+ return static_constexpr;
+ case 6:
+ return "some characters";
+ case 7:
+ return S::options;
+ case 8:
+ return const_options(3)+1;
+ default:
+ #ifdef __EXCEPTIONS
+ throw "invalid index";
+ #else
+ __builtin_trap();
+ #endif
+ }
+}
+
+void test_func() {
+ int * __ptrauth(1,1,1,const_options(0)) zero;
+ int * __ptrauth(1,1,1,const_options(1)) one;
+ int * __ptrauth(1,1,1,const_options(2)) two;
+ int * __ptrauth(1,1,1,const_options(3)) three;
+ // expected-error at -1 {{'__ptrauth' options parameter contains an empty option}}
+ // expected-error at -2 {{'__ptrauth' options parameter has a trailing comma}}
+ // expected-note at -3 {{options parameter evaluated to ','}}
+ int * __ptrauth(1,1,1,const_options(4)) four;
+ int * __ptrauth(1,1,1,const_options(5)) five;
+ int * __ptrauth(1,1,1,const_options(6)) six;
+ // expected-error at -1 {{missing comma after 'some' option in '__ptrauth' qualifier}}
+ int * __ptrauth(1,1,1,const_options(7)) seven;
+ int * __ptrauth(1,1,1,const_options(8)) eight;
+ int * __ptrauth(1,1,1,S::options) struct_access;
+ int * __ptrauth(1,1,1,2 * 3) ice;
+ // expected-error at -1 {{'__ptrauth' options parameter must be a string of comma separated flags}}
+ int * __ptrauth(1,1,1,4 + "wat,strip") arithmetic_string;
+ int * __ptrauth(1,1,1,5 + "wat,strip") arithmetic_string2;
+ // expected-error at -1 {{unknown '__ptrauth' authentication option 'trip'}}
+
+ // Handle evaluation failing
+ int * __ptrauth(1,1,1,const_options(50)) fifty;
+ // expected-error at -1 {{'__ptrauth' options parameter must be a string of comma separated flags}}
+}
More information about the cfe-commits
mailing list