[clang] [clang][PAC] add support for options parameter to __ptrauth (PR #136828)
Oliver Hunt via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 19 00:03:55 PDT 2026
https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/136828
>From 135f823ea9c65612fffdba5c314b6f50acba98a3 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sat, 13 Dec 2025 15:59:34 -0800
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/docs/PointerAuthentication.rst | 32 +-
clang/include/clang/Basic/Attr.td | 6 +-
.../clang/Basic/DiagnosticParseKinds.td | 2 +-
.../clang/Basic/DiagnosticSemaKinds.td | 25 +-
clang/include/clang/Basic/LangOptions.h | 7 -
.../include/clang/Basic/PointerAuthOptions.h | 49 +++
clang/include/clang/Parse/Parser.h | 14 +
clang/include/clang/Sema/Sema.h | 6 +-
clang/lib/AST/TypePrinter.cpp | 26 +-
clang/lib/CodeGen/CGExprConstant.cpp | 24 +-
clang/lib/Parse/ParseDecl.cpp | 16 +-
clang/lib/Sema/SemaType.cpp | 171 ++++++++-
clang/test/CodeGen/ptrauth-stripping.c | 327 ++++++++++++++++++
clang/test/Parser/ptrauth-qualifier.c | 2 +-
clang/test/Sema/ptrauth-qualifier-options.c | 112 ++++++
clang/test/Sema/ptrauth-qualifier.c | 39 ++-
.../ptrauth-qualifier-constexpr-options.cpp | 108 ++++++
17 files changed, 915 insertions(+), 51 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/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst
index bf2520b32a3a4..899bca203a137 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -427,7 +427,7 @@ purposes they are all equivalent to ``ptrauth_calls``.
``__ptrauth`` qualifier
^^^^^^^^^^^^^^^^^^^^^^^
-``__ptrauth(key, address, discriminator)`` is an extended type
+``__ptrauth(key, address, discriminator, options)`` is an extended type
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.
@@ -452,6 +452,9 @@ The qualifier's operands are as follows:
- ``discriminator`` - a constant discriminator; must be a constant expression
+- ``options`` - a constant string expression containing a list of comma
+ separated authentication options; see ``ptrauth_qualifier_options``_
+
See `Discriminators`_ for more information about discriminators.
Currently the operands must be constant-evaluable even within templates. In the
@@ -463,9 +466,9 @@ qualifiers on a parameter (after parameter type adjustment) are ignored when
deriving the type of the function. The parameter will be passed using the
default ABI for the unqualified pointer type.
-If ``x`` is an object of type ``__ptrauth(key, address, discriminator) T``,
-then the signing schema of the value stored in ``x`` is a key of ``key`` and
-a discriminator determined as follows:
+If ``x`` is an object of type ``__ptrauth(key, address, discriminator, options) T``,
+then the signing schema of the value stored in ``x`` is a key of ``key`` and a
+discriminator determined as follows:
- if ``address`` is 0, then the discriminator is ``discriminator``;
@@ -527,6 +530,27 @@ rules of C++:
indirectly. Thus, changing the address-sensitivity of a type may be
ABI-breaking even if its size and alignment do not change.
+``ptrauth_qualifier_options``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The options parameter to the ``__ptrauth`` qualifier is a string of comma
+separated modifiers to the normal authentication behavior. Currently supported
+options are
+
+- Authentication mode: This is one of ``strip``, ``sign-and-strip``, and
+ ``sign-and-auth``. The ability to modify this behavior is intended to support
+ staging ABI changes. The ``strip`` mode results in the PAC bits of a value
+ being stripped from any value and disabled any other authentication
+ operations. ``sign-and-strip`` strips an authenticated on read, but will
+ ensure a correct signature is set on assignment. Finally ``sign-and-auth`` is
+ the default mode, and provides full protection for the value.
+
+- ``authenticates-null-values``: By default the __ptrauth qualifier does not
+ sign the zero value. This permits fast implementation of null checks in the
+ common case where a null value is safe. The ``authenticates-null-values``
+ option overrides this behavior, and permits null values to be protected with
+ pointer authentication.
+
``<ptrauth.h>``
~~~~~~~~~~~~~~~
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8ab4aaa6f5781..456070adf27a9 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3765,9 +3765,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 de10dbe5d0628..8f28cfe94f3cc 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1759,7 +1759,7 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning<
InGroup<CudaCompat>;
def err_ptrauth_qualifier_bad_arg_count : Error<
- "'__ptrauth' qualifier must take between 1 and 3 arguments">;
+ "'__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 fae63cc0ba139..a7de70b3764a8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1051,6 +1051,21 @@ 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 err_ptrauth_dependent_options_string : Error<
+ "'__ptrauth' options argument cannot be a dependent value">;
+def err_ptrauth_options_parse_error : Error<
+ "%select{unexpected comma|unexpected trailing comma|unexpected character '%1'|expected a comma before '%1'}0 in '__ptrauth' options argument">;
+def err_ptrauth_unknown_authentication_option: Error<
+ "unknown '__ptrauth' authentication option '%0'">;
+def err_ptrauth_repeated_authentication_option : Error<
+ "repeated '__ptrauth' authentication %select{mode|option}0 '%1'"
+ "%select{, prior mode was '%2'|}0">;
+def note_ptrauth_evaluated_options : Note<
+ "options parameter evaluated to '%0'">;
+def note_ptrauth_previous_authentication_option : Note<
+ "previous option specified here">;
+
/// main()
// static main() is not an error in C, just in C++.
def warn_static_main : Warning<"'main' should not be declared static">,
@@ -1756,15 +1771,15 @@ def note_expr_evaluates_to : Note<
def subst_user_defined_msg : TextSubstitution<
- "%select{the message|the expression}0 in "
- "%select{a static assertion|this asm operand}0">;
+ "%select{the message|the expression|the expression}0 in "
+ "%select{a static assertion|this asm operand|'__ptrauth' options}0">;
def err_user_defined_msg_invalid : Error<
"%sub{subst_user_defined_msg}0 must be a string literal or an "
"object with 'data()' and 'size()' member functions">;
def err_user_defined_msg_missing_member_function : Error<
- "the %select{message|string}0 object in "
- "%select{this static assertion|this asm operand}0 is missing %select{"
+ "the %select{message|string|options}0 object in "
+ "%select{this static assertion|this asm operand|this '__ptrauth' options argument}0 is missing %select{"
"a 'size()' member function|"
"a 'data()' member function|"
"'data()' and 'size()' member functions}1">;
@@ -1772,7 +1787,7 @@ def err_user_defined_msg_invalid_mem_fn_ret_ty : Error<
"%sub{subst_user_defined_msg}0 must have a '%select{size|data}1()' member "
"function returning an object convertible to '%select{std::size_t|const char *}1'">;
def warn_user_defined_msg_constexpr : Warning<
- "%select{the message|the expression}0 in "
+ "%select{the message|the expression|the expression}0 in "
"%select{this static assertion|this asm operand}0 is not a constant expression">,
DefaultError, InGroup<DiagGroup<"invalid-static-assert-message">>;
def err_user_defined_msg_constexpr : Error<
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 64ec0a87089f9..f884eb8686729 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -60,13 +60,6 @@ enum class ShaderStage {
Invalid,
};
-enum class PointerAuthenticationMode : unsigned {
- None,
- Strip,
- SignAndStrip,
- SignAndAuth
-};
-
/// 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/include/clang/Basic/PointerAuthOptions.h b/clang/include/clang/Basic/PointerAuthOptions.h
index 2b920250721fc..d1e2b9d1abfea 100644
--- a/clang/include/clang/Basic/PointerAuthOptions.h
+++ b/clang/include/clang/Basic/PointerAuthOptions.h
@@ -58,6 +58,55 @@ constexpr unsigned PointerAuthKeyNone = -1;
/// the vtable type discriminator for classes derived from std::type_info.
constexpr uint16_t StdTypeInfoVTablePointerConstantDiscrimination = 0xB1EA;
+enum class PointerAuthenticationMode : unsigned {
+ None,
+ Strip,
+ SignAndStrip,
+ SignAndAuth
+};
+
+// For reviewers: Where should I actually put this? We don't really have any
+// pre-CG pointer auth specific impl files. I'm almost thinking that we should
+// have Basic/PointerAuth.cpp or something?
+
+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";
+
+inline std::optional<PointerAuthenticationMode>
+authenticationModeFromString(StringRef Option) {
+ return llvm::StringSwitch<std::optional<PointerAuthenticationMode>>(Option)
+ .Case(PointerAuthenticationOptionStrip, PointerAuthenticationMode::Strip)
+ .Case(PointerAuthenticationOptionSignAndStrip,
+ PointerAuthenticationMode::SignAndStrip)
+ .Case(PointerAuthenticationOptionSignAndAuth,
+ PointerAuthenticationMode::SignAndAuth)
+ .Default(std::nullopt);
+}
+
+inline StringRef stringForAuthenticationMode(PointerAuthenticationMode Mode) {
+ switch (Mode) {
+ case clang::PointerAuthenticationMode::Strip:
+ return PointerAuthenticationOptionStrip;
+ case clang::PointerAuthenticationMode::SignAndStrip:
+ return PointerAuthenticationOptionSignAndStrip;
+ case PointerAuthenticationMode::None:
+ // llvm_unreachable means that this path is assumed unreachable so the
+ // branch can be removed which is much worse that just defaulting to
+ // sign+auth
+ assert(0 && "We should not be stringify disabled pointer auth qualifiers");
+ case clang::PointerAuthenticationMode::SignAndAuth:
+ return PointerAuthenticationOptionSignAndAuth;
+ }
+}
+
class PointerAuthSchema {
public:
enum class Kind : unsigned {
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 5ae02e2b4e8ad..d0eb7e8eefaf5 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -16,6 +16,7 @@
#include "clang/Basic/OpenACCKinds.h"
#include "clang/Basic/OperatorPrecedence.h"
#include "clang/Lex/CodeCompletionHandler.h"
+#include "clang/Lex/LiteralSupport.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaCodeCompletion.h"
@@ -320,6 +321,19 @@ class Parser : public CodeCompletionHandler {
return PP.LookAhead(N - 1);
}
+ template <class... TokT> bool NextTokenIsStringLiteral(TokT... FollowSet) {
+ static_assert(sizeof...(TokT) > 0, "Follow set cnanot be empty");
+ for (unsigned TokenIdx = 0;; ++TokenIdx) {
+ const Token &LookAheadToken = GetLookAheadToken(TokenIdx);
+ if (LookAheadToken.isOneOf(FollowSet...))
+ return TokenIdx != 0;
+ if (!tokenIsLikeStringLiteral(LookAheadToken, getLangOpts()) ||
+ LookAheadToken.hasUDSuffix())
+ return false;
+ }
+ return true;
+ }
+
/// NextToken - This peeks ahead one token and returns it without
/// consuming it.
const Token &NextToken() { return PP.LookAhead(0); }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 832e46286194a..2aa300e997ca6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6021,7 +6021,11 @@ class Sema final : public SemaBase {
void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, Decl *Method);
void ActOnFinishDelayedMemberInitializers(Decl *Record);
- enum class StringEvaluationContext { StaticAssert = 0, Asm = 1 };
+ enum class StringEvaluationContext {
+ StaticAssert = 0,
+ Asm = 1,
+ PointerAuthOptions = 2
+ };
bool EvaluateAsString(Expr *Message, APValue &Result, ASTContext &Ctx,
StringEvaluationContext EvalContext,
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 3375630827501..64588f5881ccf 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2623,7 +2623,31 @@ void PointerAuthQualifier::print(raw_ostream &OS,
OS << "__ptrauth(";
OS << getKey();
OS << "," << unsigned(isAddressDiscriminated()) << ","
- << getExtraDiscriminator() << ")";
+ << getExtraDiscriminator();
+
+ llvm::SmallVector<StringRef, 3> Options;
+ PointerAuthenticationMode AuthenticationMode = getAuthenticationMode();
+ assert(AuthenticationMode != PointerAuthenticationMode::None &&
+ "Mode is unauthenticated but claims to be present");
+ // We only include none default authentication modes
+ if (AuthenticationMode != clang::PointerAuthenticationMode::SignAndAuth)
+ Options.push_back(stringForAuthenticationMode(getAuthenticationMode()));
+ if (isIsaPointer())
+ Options.push_back(PointerAuthenticationOptionIsaPointer);
+ if (authenticatesNullValues())
+ Options.push_back(PointerAuthenticationOptionAuthenticatesNullValues);
+ if (Options.size()) {
+ OS << ",";
+ OS << "\"";
+ for (unsigned Idx = 0; Idx < Options.size(); ++Idx) {
+ if (Idx > 0)
+ OS << ",";
+ OS << Options[Idx];
+ }
+ OS << "\"";
+ }
+
+ OS << ")";
}
std::string Qualifiers::getAsString() const {
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 0739935acd867..2ce6d9927b511 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -2167,6 +2167,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();
@@ -2200,7 +2207,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;
@@ -2248,8 +2256,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);
@@ -2257,7 +2266,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
CGPointerAuthInfo AuthInfo;
- if (EnablePtrAuthFunctionTypeDiscrimination)
+ if (IsFunction && EnablePtrAuthFunctionTypeDiscrimination)
AuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
if (AuthInfo) {
@@ -2278,18 +2287,19 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
llvm::Constant *C = CGM.getRawFunctionPointer(FD);
if (FD->getType()->isCFIUncheckedCalleeFunctionType())
C = llvm::NoCFIValue::get(cast<llvm::GlobalValue>(C));
- return PtrAuthSign(C);
+ return PtrAuthSign(C, /*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 72935f427b7f8..9feaab9fe01fb 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -20,6 +20,7 @@
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/LiteralSupport.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/RAIIObjectsForParser.h"
#include "clang/Sema/EnterExpressionEvaluationContext.h"
@@ -3160,18 +3161,25 @@ void Parser::ParsePtrauthQualifier(ParsedAttributes &Attrs) {
ArgsVector ArgExprs;
do {
- ExprResult ER = ParseAssignmentExpression();
- if (ER.isInvalid()) {
+ ExprResult ArgumentExpr;
+ if (NextTokenIsStringLiteral(tok::r_paren, tok::comma))
+ ArgumentExpr = ParseUnevaluatedStringLiteralExpression();
+ else {
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ ArgumentExpr = ParseConstantExpressionInExprEvalContext();
+ }
+ if (ArgumentExpr.isInvalid()) {
T.skipToEnd();
return;
}
- ArgExprs.push_back(ER.get());
+ ArgExprs.push_back(ArgumentExpr.get());
} while (TryConsumeToken(tok::comma));
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 2918538ac0f64..d8da14239aeca 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -44,11 +44,13 @@
#include "clang/Sema/TemplateInstCallback.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLForwardCompat.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/Support/ErrorHandling.h"
#include <bitset>
#include <optional>
+#include <span>
using namespace clang;
@@ -8631,17 +8633,163 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr,
CurType = S.Context.getVectorType(CurType, numElts, VecKind);
}
+struct PointerAuthQualifierOptions {
+ PointerAuthenticationMode AuthenticationMode =
+ PointerAuthenticationMode::SignAndAuth;
+ bool IsIsaPointer = false;
+ bool AuthenticatesNullValues = false;
+};
+
+static bool
+ParsePointerAuthQualiferOptions(Sema &S, Expr *OptionsExpr,
+ PointerAuthQualifierOptions &Result) {
+ if (!OptionsExpr)
+ return true;
+
+ if (OptionsExpr->containsErrors())
+ return false;
+
+ if (OptionsExpr->isValueDependent() || OptionsExpr->isTypeDependent()) {
+ S.Diag(OptionsExpr->getExprLoc(),
+ diag::err_ptrauth_dependent_options_string)
+ << OptionsExpr->getSourceRange();
+ return false;
+ }
+
+ std::string EvaluatedOptionsBuffer;
+ StringRef OptionsString;
+ const StringLiteral *OptionsLiteral = dyn_cast<StringLiteral>(OptionsExpr);
+ ASTContext &Ctx = S.getASTContext();
+ if (OptionsLiteral)
+ OptionsString = OptionsLiteral->getString();
+ else if (auto EvaluatedString = OptionsExpr->tryEvaluateString(Ctx)) {
+ EvaluatedString->swap(EvaluatedOptionsBuffer);
+ OptionsString = EvaluatedOptionsBuffer;
+ } else if (!S.EvaluateAsString(
+ OptionsExpr, EvaluatedOptionsBuffer, Ctx,
+ Sema::StringEvaluationContext::PointerAuthOptions,
+ /*ErrorOnInvalidMessage=*/true))
+ OptionsString = EvaluatedOptionsBuffer;
+ else
+ return false;
+
+ auto Failed = [&](SourceRange Range = {}, unsigned DiagId = 0,
+ auto... DiagArgs) {
+ if (Range.isValid())
+ (S.Diag(Range.getBegin(), DiagId) << ... << DiagArgs) << Range;
+ if (!OptionsLiteral)
+ S.Diag(OptionsExpr->getExprLoc(), diag::note_ptrauth_evaluated_options)
+ << EvaluatedOptionsBuffer << OptionsExpr->getSourceRange();
+ return false;
+ };
+
+ SmallVector<StringRef, 4> Options;
+ auto ParseString = OptionsString.trim();
+ if (ParseString.empty())
+ return true;
+
+ auto FindDiagnosticRange = [&](auto Token) {
+ if (!OptionsLiteral)
+ return OptionsExpr->getSourceRange();
+ unsigned StartOffset = Token.begin() - OptionsString.begin();
+ unsigned EndOffset = StartOffset + Token.size();
+ SourceLocation StartLoc =
+ S.getLocationOfStringLiteralByte(OptionsLiteral, StartOffset);
+ SourceLocation EndLoc =
+ S.getLocationOfStringLiteralByte(OptionsLiteral, EndOffset);
+ return SourceRange(StartLoc, EndLoc);
+ };
+
+ // Split up the options
+ auto IsOptionCharacter = [](char Ch) {
+ return llvm::isAlpha(Ch) || Ch == '-';
+ };
+ while (!ParseString.empty()) {
+ if (!Options.empty()) {
+ if (ParseString.size() <= 1 || !ParseString.consume_front(','))
+ break;
+ ParseString = ParseString.ltrim();
+ }
+ StringRef Option = ParseString.take_while(IsOptionCharacter);
+ if (Option.empty())
+ break;
+ Options.push_back(Option);
+ ParseString = ParseString.drop_front(Option.size()).ltrim();
+ }
+
+ if (!ParseString.empty()) {
+ StringRef LastOption;
+ if (!Options.empty())
+ LastOption = Options.back();
+ if (StringRef UnexpectedOption = ParseString.take_while(IsOptionCharacter);
+ !UnexpectedOption.empty()) {
+ SourceRange DiagRange = FindDiagnosticRange(UnexpectedOption);
+ return Failed(DiagRange, diag::err_ptrauth_options_parse_error,
+ /*Expected Comma*/ 3, UnexpectedOption);
+ }
+ unsigned DiagIdx = 2; // unexpected character
+ if (ParseString.starts_with(','))
+ DiagIdx = ParseString.size() == 1;
+ StringRef ErrorToken = ParseString.take_front();
+ SourceRange DiagRange = FindDiagnosticRange(ErrorToken);
+ return Failed(DiagRange, diag::err_ptrauth_options_parse_error, DiagIdx,
+ ErrorToken, LastOption);
+ }
+
+ StringRef AuthenticationModeOption;
+ StringRef IsIsaPointerOption;
+ StringRef AuthenticatesNullValuesOption;
+ for (StringRef CurrentOption : Options) {
+ StringRef *Storage = nullptr;
+ SourceRange DiagRange = FindDiagnosticRange(CurrentOption);
+ if (authenticationModeFromString(CurrentOption))
+ Storage = &AuthenticationModeOption;
+ else if (CurrentOption == PointerAuthenticationOptionIsaPointer)
+ Storage = &IsIsaPointerOption;
+ else if (CurrentOption ==
+ PointerAuthenticationOptionAuthenticatesNullValues)
+ Storage = &AuthenticatesNullValuesOption;
+ else
+ return Failed(DiagRange, diag::err_ptrauth_unknown_authentication_option,
+ CurrentOption);
+
+ if (Storage->empty()) {
+ *Storage = CurrentOption;
+ continue;
+ }
+ bool NotAuthenticationMode = !authenticationModeFromString(CurrentOption);
+ Failed(DiagRange.getBegin(),
+ diag::err_ptrauth_repeated_authentication_option,
+ NotAuthenticationMode, CurrentOption, *Storage);
+ if (OptionsLiteral) {
+ SourceRange PreviousRange = FindDiagnosticRange(*Storage);
+ S.Diag(PreviousRange.getBegin(),
+ diag::note_ptrauth_previous_authentication_option)
+ << PreviousRange;
+ }
+ return false;
+ }
+
+ Result.AuthenticationMode =
+ authenticationModeFromString(AuthenticationModeOption)
+ .value_or(PointerAuthenticationMode::SignAndAuth);
+ Result.IsIsaPointer = !IsIsaPointerOption.empty();
+ Result.AuthenticatesNullValues = !AuthenticatesNullValuesOption.empty();
+ return true;
+}
+
/// 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");
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)) {
@@ -8657,11 +8805,9 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
IsAddressDiscriminated);
IsInvalid |= !S.checkPointerAuthDiscriminatorArg(
ExtraDiscriminatorArg, PointerAuthDiscArgKind::Extra, ExtraDiscriminator);
-
- if (IsInvalid) {
- Attr.setInvalid();
- return;
- }
+ PointerAuthQualifierOptions Options;
+ IsInvalid |=
+ !ParsePointerAuthQualiferOptions(S, AuthenticationOptionsArg, Options);
if (!T->isSignableType(Ctx) && !T->isDependentType()) {
S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_invalid_target) << T;
@@ -8681,12 +8827,17 @@ 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);
+ Options.AuthenticationMode, Options.IsIsaPointer,
+ Options.AuthenticatesNullValues);
T = S.Context.getPointerAuthType(T, Qual);
}
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..19bfc0f4c46b8
--- /dev/null
+++ b/clang/test/Sema/ptrauth-qualifier-options.c
@@ -0,0 +1,112 @@
+// 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
+
+#ifndef __PTRAUTH__
+#error "the ptrauth qualifier should be available"
+#endif
+
+#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 'sign-and-strip', prior mode was 'strip'}}
+// expected-note at -2 {{previous option specified here}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer,isa-pointer") invalid16;
+// expected-error at -1 {{repeated '__ptrauth' authentication option}}
+// expected-note at -2 {{previous option specified here}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip, , isa-pointer") invalid18;
+// expected-error at -1 {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19;
+// expected-error at -1 {{unexpected trailing comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20;
+// expected-error at -1 {{unexpected trailing comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21;
+// expected-error at -1 {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22;
+// expected-error at -1 {{expected a comma before 'isa-pointer' in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23;
+// expected-error at -1 {{expected a comma before 'isa-pointer' in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip"
+ " isa-pointer") invalid24;
+// expected-error at -1 {{expected a comma before 'isa-pointer' in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\n,isa-pointer") invalid25;
+// expected-error at -1 {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") invalid26;
+// expected-error at -1 {{unexpected comma in '__ptrauth' options argument}}
+
+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;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "authenticates-null-values") valid30;
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "isa-pointer, authenticates-null-values") valid31;
+
+int *global_ptr;
+
+// Check qualifier option printing
+int *__ptrauth(1, 1, 0, "strip") * strip = &global_ptr;
+// expected-error at -1 {{int *__ptrauth(1,1,0,"strip") *}}
+int *__ptrauth(1, 1, 0, "sign-and-strip") * signAndStrip = &global_ptr;
+// expected-error at -1 {{int *__ptrauth(1,1,0,"sign-and-strip") *}}
+int *__ptrauth(1, 1, 0, "sign-and-auth") * signAndAuth = &global_ptr;
+// expected-error at -1 {{int *__ptrauth(1,1,0) *}}
+int *__ptrauth(1, 1, 0, "sign-and-auth, isa-pointer") * signAndAuthIsa = &global_ptr;
+// expected-error at -1 {{int *__ptrauth(1,1,0,"isa-pointer") *}}
+int *__ptrauth(1, 1, 0, "sign-and-strip, isa-pointer") * signAndStripIsa = &global_ptr;
+// expected-error at -1 {{int *__ptrauth(1,1,0,"sign-and-strip,isa-pointer") *'}}
+int *__ptrauth(1, 1, 0, "sign-and-strip, authenticates-null-values") * signAndAuthAuthenticatesNullValues = &global_ptr;
+// expected-error at -1 {{int *__ptrauth(1,1,0,"sign-and-strip,authenticates-null-values") *}}
+int *__ptrauth(1, 1, 0, "sign-and-strip, authenticates-null-values, isa-pointer") * signAndAuthIsaPointerAuthenticatesNullValues = &global_ptr;
+// expected-error at -1 {{int *__ptrauth(1,1,0,"sign-and-strip,isa-pointer,authenticates-null-values") *}}
+
+// Check mismatching options are identified
+void f() {
+ // default auth matches explicit use of sign-and-auth
+ int *__ptrauth(1, 1, 0) * signAndAuthDefault = signAndAuth;
+
+ int *__ptrauth(1, 1, 0, "sign-and-strip") * signAndStrip2 = strip;
+ // expected-error at -1 {{initializing 'int *__ptrauth(1,1,0,"sign-and-strip") *' with an expression of type 'int *__ptrauth(1,1,0,"strip") *' changes pointer authentication of pointee type}}
+ int *__ptrauth(1, 1, 0) * signAndAuth2 = signAndStrip;
+ // expected-error at -1 {{'int *__ptrauth(1,1,0) *' with an expression of type 'int *__ptrauth(1,1,0,"sign-and-strip") *' changes pointer authentication of pointee type}}
+ int *__ptrauth(1, 1, 0, "authenticates-null-values") * signAndAuthAuthenticatesNullValues2 = signAndAuth;
+ // expected-error at -1 {{initializing 'int *__ptrauth(1,1,0,"authenticates-null-values") *' with an expression of type 'int *__ptrauth(1,1,0) *' changes pointer authentication of pointee type}}
+ int *__ptrauth(0, 1, 0) * signAndAuth3 = signAndAuth; // different key
+ // expected-error at -1 {{initializing 'int *__ptrauth(0,1,0) *' with an expression of type 'int *__ptrauth(1,1,0) *' changes pointer authentication of pointee type}}
+ int *__ptrauth(1, 0, 0) * signAndAuth4 = signAndAuth; // different address discrimination
+ // expected-error at -1 {{initializing 'int *__ptrauth(1,0,0) *' with an expression of type 'int *__ptrauth(1,1,0) *' changes pointer authentication of pointee type}}
+ int *__ptrauth(1, 1, 1) * signAndAuth5 = signAndAuth; // different discriminator
+ // expected-error at -1 {{initializing 'int *__ptrauth(1,1,1) *' with an expression of type 'int *__ptrauth(1,1,0) *' changes pointer authentication of pointee type}}
+}
+
+
+
+
diff --git a/clang/test/Sema/ptrauth-qualifier.c b/clang/test/Sema/ptrauth-qualifier.c
index 3e568ce9f37e3..94cf1a587c00c 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 -DIS_DARWIN -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 defined(IS_DARWIN) && !__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
@@ -46,19 +48,42 @@ 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 {{the expression in '__ptrauth' options must be a string literal or an object with 'data()' and 'size()' member functions}}
+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 'sign-and-strip', prior mode was 'strip'}}
+ // expected-note at -1 {{previous option specified here}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19; // expected-error {{unexpected trailing comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20; // expected-error {{unexpected trailing comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21; // expected-error {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer,isa-pointer") invalid22; // expected-error {{repeated '__ptrauth' authentication option 'isa-pointer'}}
+ // expected-note at -1 {{previous option specified here}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid23; // expected-error {{expected a comma before 'isa-pointer' in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip"
+ " isa-pointer") invalid24; // expected-error {{expected a comma before 'isa-pointer' in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,,isa-pointer") invalid25; // expected-error {{unexpected comma in '__ptrauth' options argument}}
+int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,isa-pointer,") invalid26; // expected-error {{unexpected trailing comma in '__ptrauth' options argument}}
int * __ptrauth(VALID_DATA_KEY) valid0;
int * __ptrauth(VALID_DATA_KEY) *valid1;
__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..2bba455f46941
--- /dev/null
+++ b/clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp
@@ -0,0 +1,108 @@
+// 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";
+};
+
+struct string_view {
+ int S;
+ const char* D;
+ constexpr string_view() : S(0), D(0){}
+ constexpr string_view(const char* Str) : S(__builtin_strlen(Str)), D(Str) {}
+ constexpr string_view(int Size, const char* Str) : S(Size), D(Str) {}
+ constexpr int size() const {
+ return S;
+ }
+ constexpr const char* data() const {
+ return D;
+ }
+};
+template <class StringType> 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<StringType>(3)+1;
+ default:
+ #ifdef __EXCEPTIONS
+ throw "invalid index";
+ #else
+ __builtin_trap();
+ #endif
+ }
+}
+
+// When we support dependent pointer auth qualifiers this can become a template
+// function rather than manually duplicating it.
+void test_func_charptr() {
+ using StringType = const char*;
+ int * __ptrauth(1,1,1,const_options<StringType>(0)) zero;
+ int * __ptrauth(1,1,1,const_options<StringType>(1)) one;
+ int * __ptrauth(1,1,1,const_options<StringType>(2)) two;
+ int * __ptrauth(1,1,1,const_options<StringType>(3)) three;
+ // expected-error at -1 {{unexpected trailing comma in '__ptrauth' options argument}}
+ // expected-note at -2 {{options parameter evaluated to ','}}
+ int * __ptrauth(1,1,1,const_options<StringType>(4)) four;
+ int * __ptrauth(1,1,1,const_options<StringType>(5)) five;
+ int * __ptrauth(1,1,1,const_options<StringType>(6)) six;
+ // expected-error at -1 {{expected a comma before 'characters' in '__ptrauth' options argument}}
+ // expected-note at -2 {{options parameter evaluated to 'some characters'}}
+ int * __ptrauth(1,1,1,const_options<StringType>(7)) seven;
+ int * __ptrauth(1,1,1,const_options<StringType>(8)) eight;
+ int * __ptrauth(1,1,1,2 * 3) ice;
+ // expected-error at -1 {{the expression in '__ptrauth' options must be a string literal or an object with 'data()' and 'size()' member functions}}
+ 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'}}
+ // expected-note at -2 {{options parameter evaluated to 'trip'}}
+
+ // Handle evaluation failing
+ int * __ptrauth(1,1,1,const_options<StringType>(50)) fifty;
+ // expected-error at -1 {{the expression in '__ptrauth' options must be a string literal or an object with 'data()' and 'size()' member functions}}
+}
+
+void test_func_string_view() {
+ using StringType = string_view;
+ int * __ptrauth(1,1,1,const_options<StringType>(0)) zero;
+ int * __ptrauth(1,1,1,const_options<StringType>(1)) one;
+ int * __ptrauth(1,1,1,const_options<StringType>(2)) two;
+ int * __ptrauth(1,1,1,const_options<StringType>(3)) three;
+ // expected-error at -1 {{unexpected trailing comma in '__ptrauth' options argument}}
+ // expected-note at -2 {{options parameter evaluated to ','}}
+ int * __ptrauth(1,1,1,const_options<StringType>(4)) four;
+ int * __ptrauth(1,1,1,const_options<StringType>(5)) five;
+ int * __ptrauth(1,1,1,const_options<StringType>(6)) six;
+ // expected-error at -1 {{expected a comma before 'characters' in '__ptrauth' options argument}}
+ // expected-note at -2 {{options parameter evaluated to 'some characters'}}
+ int * __ptrauth(1,1,1,const_options<StringType>(7)) seven;
+ int * __ptrauth(1,1,1,const_options<StringType>(8)) eight;
+ int * __ptrauth(1,1,1,2 * 3) ice;
+ // expected-error at -1 {{the expression in '__ptrauth' options must be a string literal or an object with 'data()' and 'size()' member functions}}
+ 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'}}
+ // expected-note at -2 {{options parameter evaluated to 'trip'}}
+
+ // Handle evaluation failing
+ int * __ptrauth(1,1,1,const_options<StringType>(50)) fifty;
+ // expected-error at -1 {{the expression in '__ptrauth' options must be a string literal or an object with 'data()' and 'size()' member functions}}
+}
More information about the cfe-commits
mailing list