[clang] [clang][ptrauth] Add support for querying the ptrauth schema of a type (PR #138482)

Oliver Hunt via cfe-commits cfe-commits at lists.llvm.org
Sun May 4 21:13:03 PDT 2025


https://github.com/ojhunt created https://github.com/llvm/llvm-project/pull/138482

This adds a number of builtins to query the ptrauth schema of an arbitrary type in a way that can be fed into other ptrauth qualifiers.

>From e84310f8a57a3f9b75dfc7260137e984d7f55f87 Mon Sep 17 00:00:00 2001
From: Oliver Hunt <oliver at apple.com>
Date: Sun, 4 May 2025 21:07:33 -0700
Subject: [PATCH] [clang][ptrauth] Add support for querying the ptrauth schema
 of a type

This adds a number of builtins to query the ptrauth schema of an arbitrary
type in a way that can be fed into other ptrauth qualifiers.
---
 clang/docs/PointerAuthentication.rst          |  18 +++
 clang/include/clang/AST/ASTContext.h          |  12 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +
 clang/include/clang/Basic/LangOptions.def     |   1 +
 clang/include/clang/Basic/LangOptions.h       |  11 ++
 clang/include/clang/Basic/TokenKinds.def      |   5 +
 clang/include/clang/Parse/Parser.h            |   2 +
 clang/lib/AST/ASTContext.cpp                  |  60 ++++++++++
 clang/lib/AST/ExprConstant.cpp                |  72 ++++++++++++
 clang/lib/AST/ItaniumMangle.cpp               |   8 ++
 clang/lib/CodeGen/CGExprScalar.cpp            |  10 ++
 clang/lib/Frontend/CompilerInvocation.cpp     |   4 +-
 clang/lib/Parse/ParseExpr.cpp                 |  41 ++++++-
 clang/lib/Sema/SemaExpr.cpp                   |  45 +++++++-
 clang/test/CodeGen/ptrauth-queries.c          |  44 ++++++++
 clang/test/Sema/ptrauth-type-query.c          |  83 ++++++++++++++
 clang/test/SemaCXX/ptrauth-type-query.cpp     | 105 ++++++++++++++++++
 17 files changed, 518 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/CodeGen/ptrauth-queries.c
 create mode 100644 clang/test/Sema/ptrauth-type-query.c
 create mode 100644 clang/test/SemaCXX/ptrauth-type-query.cpp

diff --git a/clang/docs/PointerAuthentication.rst b/clang/docs/PointerAuthentication.rst
index 41818d43ac687..b7fcbf7a88884 100644
--- a/clang/docs/PointerAuthentication.rst
+++ b/clang/docs/PointerAuthentication.rst
@@ -499,6 +499,24 @@ type.  Implementations are not required to make all bits of the result equally
 significant; in particular, some implementations are known to not leave
 meaningful data in the low bits.
 
+``__ptrauth type queries``
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+There are a number of builtins that can be used to query the ptrauth qualifier
+parameters of a type, including those configured implicitly. These are:
+
+.. code-block:: c
+__builtin_ptrauth_has_authentication(type)
+__builtin_ptrauth_schema_key(type)
+__builtin_ptrauth_schema_is_address_discriminated(type)
+__builtin_ptrauth_schema_extra_discriminator(type)
+__builtin_ptrauth_schema_options(type)
+
+All these builtins are compile time constants. The schema queries are only valid
+on types that have some form of pointer authentication, including implicit
+authentication as is present of function pointers. Each schema query returns a
+value of the appropriate type for the relevant parameter to the __ptrauth
+qualifier.
 
 
 Alternative Implementations
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 50083b055199e..6e68ed238c01f 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1344,6 +1344,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// Return the "other" type-specific discriminator for the given type.
   uint16_t getPointerAuthTypeDiscriminator(QualType T);
 
+  /// Produces the canonical "options" string for the given PointerAuthQualifier
+  /// such that using it explicitly in a __ptrauth qualifier would result in as
+  /// identical configuration
+  std::string getPointerAuthOptionsString(const PointerAuthQualifier &PAQ);
+
   /// Apply Objective-C protocol qualifiers to the given type.
   /// \param allowOnPointerType specifies if we can apply protocol
   /// qualifiers on ObjCObjectPointerType. It can be set to true when
@@ -1682,6 +1687,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
 
   QualType adjustStringLiteralBaseType(QualType StrLTy) const;
 
+  /// Synthesizes a PointerAuthQualifier representing the actual authentication
+  /// policy for the given type. This may be either the schema specified
+  /// explicitly via the __ptrauth qualified in the source, or the implicit
+  /// schema associated with function pointers and similar.
+  std::optional<PointerAuthQualifier>
+  getExplicitOrImplicitPointerAuth(QualType T);
+
 private:
   /// Return a normal function type with a typed argument list.
   QualType getFunctionTypeInternal(QualType ResultTy, ArrayRef<QualType> Args,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e5a7cdc14a737..e6111f1c91579 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1039,6 +1039,8 @@ def err_ptrauth_address_discrimination_invalid : Error<
   "invalid address discrimination flag '%0'; '__ptrauth' requires '0' or '1'">;
 def err_ptrauth_extra_discriminator_invalid : Error<
   "invalid extra discriminator flag '%0'; '__ptrauth' requires a value between '0' and '%1'">;
+def err_ptrauth_query_type_has_no_pointer_authentication
+    : Error<"argument to %0 parameter is not an authenticated value">;
 
 /// main()
 // static main() is not an error in C, just in C++.
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 930c1c06d1a76..cd50c96253631 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -177,6 +177,7 @@ LANGOPT(PointerAuthInitFiniAddressDiscrimination, 1, 0,
         "incorporate address discrimination in authenticated function pointers in init/fini arrays")
 LANGOPT(PointerAuthELFGOT, 1, 0, "authenticate pointers from GOT")
 LANGOPT(AArch64JumpTableHardening, 1, 0, "use hardened lowering for jump-table dispatch")
+LANGOPT(PointerAuthFunctionKey, 16, 0, "authentication key for function pointers")
 
 LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes")
 LANGOPT(ExperimentalLateParseAttributes, 1, 0, "experimental late parsing of attributes")
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index 1bfc0d8e88556..826b4cf60f776 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/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index fb53d88deea4a..805a90f1b2c72 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -601,6 +601,11 @@ KEYWORD(__private_extern__          , KEYALL)
 KEYWORD(__module_private__          , KEYALL)
 
 UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_type_discriminator, PtrAuthTypeDiscriminator, KEYALL)
+UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_has_authentication, PtrAuthHasAuthentication, KEYALL)
+UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_key, PtrAuthSchemaKey, KEYALL)
+UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_is_address_discriminated, PtrAuthSchemaIsAddressDiscriminated, KEYALL)
+UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_extra_discriminator, PtrAuthSchemaExtraDiscriminator, KEYALL)
+UNARY_EXPR_OR_TYPE_TRAIT(__builtin_ptrauth_schema_options, PtrAuthSchemaOptions, KEYALL)
 
 // Extension that will be enabled for Microsoft, Borland and PS4, but can be
 // disabled via '-fno-declspec'.
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 46a2d26beb7f9..3365b2d97c0ca 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3969,7 +3969,9 @@ class Parser : public CodeCompletionHandler {
   ExprResult ParseArrayTypeTrait();
   ExprResult ParseExpressionTrait();
 
+  ExprResult ParseBuiltinUnaryExprOrTypeTrait(UnaryExprOrTypeTrait ExprKind);
   ExprResult ParseBuiltinPtrauthTypeDiscriminator();
+  ExprResult ParseBuiltinPtrauthQuery(tok::TokenKind Token);
 
   //===--------------------------------------------------------------------===//
   // Preprocessor code-completion pass-through
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index ae136ae271882..c6e3ea559a0f4 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -96,6 +96,7 @@
 #include <map>
 #include <memory>
 #include <optional>
+#include <sstream>
 #include <string>
 #include <tuple>
 #include <utility>
@@ -9601,6 +9602,65 @@ ObjCInterfaceDecl *ASTContext::getObjCProtocolDecl() const {
   return ObjCProtocolClassDecl;
 }
 
+std::optional<PointerAuthQualifier>
+ASTContext::getExplicitOrImplicitPointerAuth(QualType T) {
+  auto ExplicitQualifier = T.getPointerAuth();
+  if (ExplicitQualifier.isPresent())
+    return ExplicitQualifier;
+  if (T->isDependentType()) {
+    return std::nullopt;
+  }
+  // FIXME: The more we expand pointer auth support, the more it becomes clear
+  // the codegen option's PointerAuth info belongs in LangOpts
+  if (!LangOpts.PointerAuthCalls)
+    return PointerAuthQualifier();
+  if (T->isFunctionPointerType() || T->isFunctionReferenceType())
+    T = T->getPointeeType();
+  if (!T->isFunctionType())
+    return PointerAuthQualifier();
+  int ExtraDiscriminator = 0;
+  if (LangOpts.PointerAuthFunctionTypeDiscrimination) {
+    ExtraDiscriminator = getPointerAuthTypeDiscriminator(T);
+  }
+  return PointerAuthQualifier::Create(
+      LangOpts.PointerAuthFunctionKey, false, ExtraDiscriminator,
+      PointerAuthenticationMode::SignAndAuth,
+      /*isIsaPointer=*/false, /*authenticatesNullValues=*/false);
+}
+
+std::string
+ASTContext::getPointerAuthOptionsString(const PointerAuthQualifier &PAQ) {
+  llvm::SmallVector<llvm::StringLiteral, 4> Options;
+  switch (PAQ.getAuthenticationMode()) {
+  case PointerAuthenticationMode::Strip:
+    Options.push_back(PointerAuthenticationOptionStrip);
+    break;
+  case PointerAuthenticationMode::SignAndStrip:
+    Options.push_back(PointerAuthenticationOptionSignAndStrip);
+    break;
+  case PointerAuthenticationMode::SignAndAuth:
+    // Just default to not listing this explicitly
+    break;
+  default:
+    llvm_unreachable("Invalid authentication mode");
+  }
+  if (PAQ.isIsaPointer())
+    Options.push_back(PointerAuthenticationOptionIsaPointer);
+  if (PAQ.authenticatesNullValues())
+    Options.push_back(PointerAuthenticationOptionAuthenticatesNullValues);
+  if (Options.empty())
+    return std::string();
+  if (Options.size() == 1)
+    return Options[0].str();
+  std::ostringstream Buffer;
+  Buffer << Options[0].str();
+  for (unsigned i = 1; i < Options.size(); i++) {
+    Buffer << ',';
+    Buffer << Options[i].str();
+  }
+  return Buffer.str();
+}
+
 //===----------------------------------------------------------------------===//
 // __builtin_va_list Construction Functions
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b79d8c197fe7d..4f61f5d4bbdd1 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9561,6 +9561,41 @@ class PointerExprEvaluator
     return true;
   }
 
+  bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) {
+    // This is the only UETT we evaluate here.
+    assert(E->getKind() == UETT_PtrAuthSchemaOptions &&
+           "Unknown UnaryExprOrTypeTraitExpr");
+
+    // Note for review: there are other UETTs down the road
+    // that make a switch make sense, but for now this is the only
+    // one should this just be an
+    //   if (E->getKind() != UETT_PtrAuthSchemaOptions)
+    //     return false;
+    ASTContext &Ctx = Info.Ctx;
+    switch (E->getKind()) {
+    case UETT_PtrAuthSchemaOptions: {
+      auto ArgumentType = E->getArgumentType();
+      auto Qualifier = Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
+      if (!Qualifier)
+        return false;
+      if (!Qualifier->isPresent())
+        return false;
+      auto OptionsString = Ctx.getPointerAuthOptionsString(*Qualifier);
+      QualType StrTy =
+          Ctx.getStringLiteralArrayType(Ctx.CharTy, OptionsString.length());
+      StringLiteral *OptionsLit =
+          StringLiteral::Create(Ctx, OptionsString, StringLiteralKind::Ordinary,
+                                /*Pascal=*/false, StrTy, SourceLocation());
+      APValue OptionsVal(OptionsLit, CharUnits::Zero(),
+                         {APValue::LValuePathEntry::ArrayIndex(0)},
+                         /*OnePastTheEnd=*/false);
+      return Success(OptionsVal, E);
+    }
+    default:
+      return false;
+    }
+  }
+
   bool VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *E) {
     std::string ResultStr = E->ComputeName(Info.Ctx);
 
@@ -14878,6 +14913,43 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
     return Success(
         Info.Ctx.getPointerAuthTypeDiscriminator(E->getArgumentType()), E);
   }
+  case UETT_PtrAuthHasAuthentication: {
+    auto ArgumentType = E->getArgumentType();
+    auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
+    if (!Qualifier)
+      return false;
+    return Success(Qualifier->isPresent(), E);
+  }
+  case UETT_PtrAuthSchemaKey: {
+    auto ArgumentType = E->getArgumentType();
+    auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
+    if (!Qualifier)
+      return false;
+    if (!Qualifier->isPresent())
+      return false;
+    return Success(Qualifier->getKey(), E);
+  }
+  case UETT_PtrAuthSchemaIsAddressDiscriminated: {
+    auto ArgumentType = E->getArgumentType();
+    auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
+    if (!Qualifier)
+      return false;
+    if (!Qualifier->isPresent())
+      return false;
+    return Success(Qualifier->isAddressDiscriminated(), E);
+  }
+  case UETT_PtrAuthSchemaExtraDiscriminator: {
+    auto ArgumentType = E->getArgumentType();
+    auto Qualifier = Info.Ctx.getExplicitOrImplicitPointerAuth(ArgumentType);
+    if (!Qualifier)
+      return false;
+    if (!Qualifier->isPresent())
+      return false;
+    return Success(Qualifier->getExtraDiscriminator(), E);
+  }
+  case UETT_PtrAuthSchemaOptions:
+    llvm_unreachable(
+        "UETT_PtrAuthSchemaOptions should be evaluated as a pointer");
   case UETT_VecStep: {
     QualType Ty = E->getTypeOfArgument();
 
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 33a8728728574..903f6e1d69c97 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5426,6 +5426,14 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
       Diags.Report(E->getExprLoc(), DiagID) << getTraitSpelling(SAE->getKind());
       return;
     }
+    case UETT_PtrAuthHasAuthentication:
+    case UETT_PtrAuthSchemaKey:
+    case UETT_PtrAuthSchemaIsAddressDiscriminated:
+    case UETT_PtrAuthSchemaExtraDiscriminator:
+    case UETT_PtrAuthSchemaOptions: {
+      MangleExtensionBuiltin(SAE);
+      break;
+    }
     }
     break;
   }
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 15a6177746403..b9802d38c20c4 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -3573,6 +3573,16 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr(
   } else if (E->getKind() == UETT_VectorElements) {
     auto *VecTy = cast<llvm::VectorType>(ConvertType(E->getTypeOfArgument()));
     return Builder.CreateElementCount(CGF.SizeTy, VecTy->getElementCount());
+  } else if (E->getKind() == clang::UETT_PtrAuthSchemaOptions) {
+    auto PointerAuth =
+        CGF.getContext().getExplicitOrImplicitPointerAuth(E->getArgumentType());
+    assert(PointerAuth);
+    auto OptionsString =
+        CGF.getContext().getPointerAuthOptionsString(*PointerAuth);
+    ConstantAddress C =
+        CGF.CGM.GetAddrOfConstantCString(OptionsString, OptionsString.c_str());
+    return CGF.Builder.CreateBitCast(C.getPointer(),
+                                     CGF.getTypes().ConvertType(E->getType()));
   }
 
   // If this isn't sizeof(vla), the result must be constant; use the constant
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index c7d11e6027ccf..5fbce672afcc8 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1530,7 +1530,7 @@ void CompilerInvocation::setDefaultPointerAuthOptions(
     using Discrimination = PointerAuthSchema::Discrimination;
     // If you change anything here, be sure to update <ptrauth.h>.
     Opts.FunctionPointers = PointerAuthSchema(
-        Key::ASIA, false,
+        (Key)LangOpts.PointerAuthFunctionKey, false,
         LangOpts.PointerAuthFunctionTypeDiscrimination ? Discrimination::Type
                                                        : Discrimination::None);
 
@@ -3581,6 +3581,8 @@ static void ParsePointerAuthArgs(LangOptions &Opts, ArgList &Args,
   Opts.PointerAuthELFGOT = Args.hasArg(OPT_fptrauth_elf_got);
   Opts.AArch64JumpTableHardening =
       Args.hasArg(OPT_faarch64_jump_table_hardening);
+  Opts.PointerAuthFunctionKey =
+      static_cast<unsigned>(PointerAuthSchema::ARM8_3Key::ASIA);
 }
 
 /// Check if input file kind and language standard are compatible.
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 4b5d677f4ba87..e8753d9ba46e3 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -861,7 +861,14 @@ bool Parser::isRevertibleTypeTrait(const IdentifierInfo *II,
   return false;
 }
 
-ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
+ExprResult
+Parser::ParseBuiltinUnaryExprOrTypeTrait(UnaryExprOrTypeTrait ExprKind) {
+  assert(ExprKind == UETT_PtrAuthTypeDiscriminator ||
+         ExprKind == UETT_PtrAuthHasAuthentication ||
+         ExprKind == UETT_PtrAuthSchemaKey ||
+         ExprKind == UETT_PtrAuthSchemaIsAddressDiscriminated ||
+         ExprKind == UETT_PtrAuthSchemaExtraDiscriminator ||
+         ExprKind == UETT_PtrAuthSchemaOptions);
   SourceLocation Loc = ConsumeToken();
 
   BalancedDelimiterTracker T(*this, tok::l_paren);
@@ -877,10 +884,33 @@ ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
   SourceLocation EndLoc = Tok.getLocation();
   T.consumeClose();
   return Actions.ActOnUnaryExprOrTypeTraitExpr(
-      Loc, UETT_PtrAuthTypeDiscriminator,
+      Loc, ExprKind,
       /*isType=*/true, Ty.get().getAsOpaquePtr(), SourceRange(Loc, EndLoc));
 }
 
+ExprResult Parser::ParseBuiltinPtrauthTypeDiscriminator() {
+  return ParseBuiltinUnaryExprOrTypeTrait(UETT_PtrAuthTypeDiscriminator);
+}
+
+ExprResult Parser::ParseBuiltinPtrauthQuery(tok::TokenKind Token) {
+  switch (Token) {
+  case tok::kw___builtin_ptrauth_has_authentication:
+    return ParseBuiltinUnaryExprOrTypeTrait(UETT_PtrAuthHasAuthentication);
+  case tok::kw___builtin_ptrauth_schema_key:
+    return ParseBuiltinUnaryExprOrTypeTrait(UETT_PtrAuthSchemaKey);
+  case tok::kw___builtin_ptrauth_schema_is_address_discriminated:
+    return ParseBuiltinUnaryExprOrTypeTrait(
+        UETT_PtrAuthSchemaIsAddressDiscriminated);
+  case tok::kw___builtin_ptrauth_schema_extra_discriminator:
+    return ParseBuiltinUnaryExprOrTypeTrait(
+        UETT_PtrAuthSchemaExtraDiscriminator);
+  case tok::kw___builtin_ptrauth_schema_options:
+    return ParseBuiltinUnaryExprOrTypeTrait(UETT_PtrAuthSchemaOptions);
+  default:
+    llvm_unreachable("Unexpected token");
+  }
+}
+
 /// Parse a cast-expression, or, if \pisUnaryExpression is true, parse
 /// a unary-expression.
 ///
@@ -1858,6 +1888,13 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
   case tok::kw___builtin_ptrauth_type_discriminator:
     return ParseBuiltinPtrauthTypeDiscriminator();
 
+  case tok::kw___builtin_ptrauth_has_authentication:
+  case tok::kw___builtin_ptrauth_schema_key:
+  case tok::kw___builtin_ptrauth_schema_is_address_discriminated:
+  case tok::kw___builtin_ptrauth_schema_extra_discriminator:
+  case tok::kw___builtin_ptrauth_schema_options:
+    return ParseBuiltinPtrauthQuery(SavedKind);
+
   case tok::kw___is_lvalue_expr:
   case tok::kw___is_rvalue_expr:
     if (NotPrimaryExpression)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d72d97addfac2..a55beee18fe3e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4200,6 +4200,23 @@ static bool checkPtrAuthTypeDiscriminatorOperandType(Sema &S, QualType T,
   return false;
 }
 
+static bool CheckPtrAuthQuery(Sema &S, StringRef KWName,
+                              UnaryExprOrTypeTrait Kind, QualType T,
+                              SourceLocation Loc, SourceRange ArgRange) {
+  if (Kind == UETT_PtrAuthHasAuthentication)
+    return false;
+
+  auto PointerAuth = S.Context.getExplicitOrImplicitPointerAuth(T);
+  if (!PointerAuth)
+    return false;
+  if (!PointerAuth->isPresent()) {
+    S.Diag(Loc, diag::err_ptrauth_query_type_has_no_pointer_authentication)
+        << KWName << ArgRange;
+    return true;
+  }
+  return false;
+}
+
 static bool CheckExtensionTraitOperandType(Sema &S, QualType T,
                                            SourceLocation Loc,
                                            SourceRange ArgRange,
@@ -4651,6 +4668,15 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
                                        ExprKind))
     return true;
 
+  if (ExprKind == UETT_PtrAuthHasAuthentication ||
+      ExprKind == UETT_PtrAuthSchemaKey ||
+      ExprKind == UETT_PtrAuthSchemaIsAddressDiscriminated ||
+      ExprKind == UETT_PtrAuthSchemaExtraDiscriminator ||
+      ExprKind == UETT_PtrAuthSchemaOptions) {
+    return CheckPtrAuthQuery(*this, KWName, ExprKind, ExprType, OpLoc,
+                             ExprRange);
+  }
+
   if (ExprType->isVariablyModifiedType() && FunctionScopes.size() > 1) {
     if (auto *TT = ExprType->getAs<TypedefType>()) {
       for (auto I = FunctionScopes.rbegin(),
@@ -4700,9 +4726,24 @@ ExprResult Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo,
       TInfo->getType()->isVariablyModifiedType())
     TInfo = TransformToPotentiallyEvaluated(TInfo);
 
+  QualType ResultTy;
+  if (ExprKind == UETT_PtrAuthSchemaOptions) {
+    ResultTy = Context.CharTy.withConst();
+    ResultTy = Context.adjustStringLiteralBaseType(ResultTy);
+    ResultTy = Context.getPointerType(ResultTy);
+  } else if (ExprKind == UETT_PtrAuthHasAuthentication ||
+             ExprKind == UETT_PtrAuthSchemaIsAddressDiscriminated) {
+    ResultTy = Context.BoolTy;
+  } else if (ExprKind == UETT_PtrAuthSchemaKey ||
+             ExprKind == UETT_PtrAuthSchemaExtraDiscriminator) {
+    ResultTy = Context.UnsignedIntTy;
+  } else {
+    ResultTy = Context.getSizeType();
+  }
+
   // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t.
-  return new (Context) UnaryExprOrTypeTraitExpr(
-      ExprKind, TInfo, Context.getSizeType(), OpLoc, R.getEnd());
+  return new (Context)
+      UnaryExprOrTypeTraitExpr(ExprKind, TInfo, ResultTy, OpLoc, R.getEnd());
 }
 
 ExprResult
diff --git a/clang/test/CodeGen/ptrauth-queries.c b/clang/test/CodeGen/ptrauth-queries.c
new file mode 100644
index 0000000000000..eda6a9804bac5
--- /dev/null
+++ b/clang/test/CodeGen/ptrauth-queries.c
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm %s  -o - | FileCheck %s
+
+#include <ptrauth.h>
+
+typedef void* __ptrauth(2,1,1234,"strip") TestPtr;
+
+// CHECK: @strip = private unnamed_addr constant [6 x i8] c"strip\00", align 1
+
+int check_has_auth() {
+  return __builtin_ptrauth_has_authentication(TestPtr);
+}
+
+// CHECK-LABEL: check_has_auth
+// CHECK: ret i32 1
+
+int check_has_no_auth() {
+  return __builtin_ptrauth_has_authentication(float);
+}
+// CHECK-LABEL: check_has_no_auth
+// CHECK: ret i32 0
+
+int check_ptrauth_schema_key() {
+  return __builtin_ptrauth_schema_key(TestPtr);
+}
+// CHECK-LABEL: check_ptrauth_schema_key
+// ret i32 2
+
+int check_ptrauth_schema_is_address_discriminated() {
+  return __builtin_ptrauth_schema_is_address_discriminated(TestPtr);
+}
+// CHECK-LABEL: check_ptrauth_schema_is_address_discriminated
+// CHECK: ret i32 1
+
+int check_ptrauth_schema_extra_discriminator() {
+  return __builtin_ptrauth_schema_extra_discriminator(TestPtr);
+}
+// CHECK-LABEL: check_ptrauth_schema_extra_discriminator
+// CHECK: ret i32 1234
+
+const char* check_ptrauth_schema_options() {
+  return __builtin_ptrauth_schema_options(TestPtr);
+}
+// CHECK-LABEL: check_ptrauth_schema_options
+// CHECK: ret ptr @strip
diff --git a/clang/test/Sema/ptrauth-type-query.c b/clang/test/Sema/ptrauth-type-query.c
new file mode 100644
index 0000000000000..6d4418c01d19e
--- /dev/null
+++ b/clang/test/Sema/ptrauth-type-query.c
@@ -0,0 +1,83 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -std=c23 -verify -fptrauth-calls -fptrauth-intrinsics %s
+
+#include <ptrauth.h>
+
+struct __attribute__((ptrauth_struct(3, 100))) PtrauthStruct {
+  void *ptr;
+};
+
+struct NoPtrauthStruct {
+  void *ptr;
+};
+
+void test_func(void) {
+   int *no_auth = 0;
+   _Static_assert(!__builtin_ptrauth_has_authentication(typeof(no_auth)));
+   __builtin_ptrauth_schema_key(typeof(no_auth));
+   // expected-error at -1 {{argument to __builtin_ptrauth_schema_key parameter is not an authenticated value}}
+   __builtin_ptrauth_schema_is_address_discriminated(typeof(no_auth));
+   // expected-error at -1 {{argument to __builtin_ptrauth_schema_is_address_discriminated parameter is not an authenticated value}}
+   __builtin_ptrauth_schema_extra_discriminator(typeof(no_auth));
+   // expected-error at -1 {{argument to __builtin_ptrauth_schema_extra_discriminator parameter is not an authenticated value}}
+   __builtin_ptrauth_schema_options(typeof(no_auth));
+   // expected-error at -1 {{argument to __builtin_ptrauth_schema_options parameter is not an authenticated value}}
+
+
+   int *__ptrauth(1,0,1) no_addr_disc = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(typeof(no_addr_disc)));
+   _Static_assert(__builtin_ptrauth_schema_key(typeof(no_addr_disc)) == 1);
+   _Static_assert(!__builtin_ptrauth_schema_is_address_discriminated(typeof(no_addr_disc)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(typeof(no_addr_disc)) == 1);
+   int *__ptrauth(1,1,2) addr_disc = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(typeof(addr_disc)));
+   _Static_assert(__builtin_ptrauth_schema_key(typeof(addr_disc)) == 1);
+   _Static_assert(__builtin_ptrauth_schema_is_address_discriminated(typeof(addr_disc)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(typeof(addr_disc)) == 2);
+   int *__ptrauth(2,1,3) key2 = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(typeof(key2)));
+   _Static_assert(__builtin_ptrauth_schema_key(typeof(key2)) == 2);
+   _Static_assert(__builtin_ptrauth_schema_is_address_discriminated(typeof(key2)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(typeof(key2)) == 3);
+
+   int *__ptrauth(1, 0, 4, "strip") strip = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(typeof(strip)));
+   _Static_assert(__builtin_ptrauth_schema_key(typeof(strip)) == 1);
+   _Static_assert(!__builtin_ptrauth_schema_is_address_discriminated(typeof(strip)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(typeof(strip)) == 4);
+   _Static_assert(__builtin_strcmp("strip", __builtin_ptrauth_schema_options(typeof(strip))) == 0);
+
+   int *__ptrauth(1, 0, 4, __builtin_ptrauth_schema_options(typeof(strip))) strip2 = 0;
+   __auto_type test_ptr = &strip;
+   // Verify matching pointer auth schema
+   test_ptr = &strip2;
+
+   void (*normal_func_ptr)(int) = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(typeof(normal_func_ptr)));
+   _Static_assert(__builtin_ptrauth_schema_key(typeof(normal_func_ptr)) == ptrauth_key_function_pointer);
+   _Static_assert(!__builtin_ptrauth_schema_is_address_discriminated(typeof(normal_func_ptr)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(typeof(normal_func_ptr)) == 0);
+
+   void (* __ptrauth(1,1,5) explicit_signed_func_ptr)(int)  = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(typeof(explicit_signed_func_ptr)));
+   _Static_assert(__builtin_ptrauth_schema_key(typeof(explicit_signed_func_ptr)) == 1);
+   _Static_assert(__builtin_ptrauth_schema_is_address_discriminated(typeof(explicit_signed_func_ptr)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(typeof(explicit_signed_func_ptr)) == 5);
+
+   _Static_assert(!__builtin_ptrauth_has_authentication(float));
+
+   struct PtrauthStruct *S;
+   _Static_assert(__builtin_ptrauth_has_authentication(typeof(S)));
+   _Static_assert(__builtin_ptrauth_has_authentication(struct PtrauthStruct *));
+   _Static_assert(__builtin_ptrauth_schema_key(struct PtrauthStruct *) == 3);
+   _Static_assert(__builtin_ptrauth_schema_is_address_discriminated(struct PtrauthStruct *) == 0);
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(struct PtrauthStruct *) == 100);
+   _Static_assert(__builtin_strcmp("", __builtin_ptrauth_schema_options(struct PtrauthStruct *)) == 0);
+   struct PtrauthStruct * __ptrauth(1, 1, 101, "strip") overrideAuth;
+   _Static_assert(__builtin_ptrauth_schema_key(typeof(overrideAuth)) == 1);
+   _Static_assert(__builtin_ptrauth_schema_is_address_discriminated(typeof(overrideAuth)) == 1);
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(typeof(overrideAuth)) == 101);
+   _Static_assert(__builtin_strcmp("strip", __builtin_ptrauth_schema_options(typeof(overrideAuth))) == 0);
+
+   _Static_assert(__builtin_ptrauth_schema_key(struct NoPtrauthStruct *) == 1);
+   // expected-error at -1 {{argument to __builtin_ptrauth_schema_key parameter is not an authenticated value}}
+}
diff --git a/clang/test/SemaCXX/ptrauth-type-query.cpp b/clang/test/SemaCXX/ptrauth-type-query.cpp
new file mode 100644
index 0000000000000..4f5f24a7d39a1
--- /dev/null
+++ b/clang/test/SemaCXX/ptrauth-type-query.cpp
@@ -0,0 +1,105 @@
+// RUN: %clang_cc1 -triple arm64-apple-ios -fsyntax-only -std=c++2b -verify -fptrauth-calls -fptrauth-intrinsics %s
+
+#include <ptrauth.h>
+
+void test_func(void) {
+   int *no_auth = 0;
+   _Static_assert(!__builtin_ptrauth_has_authentication(decltype(no_auth)));
+   __builtin_ptrauth_schema_key(decltype(no_auth));
+   // expected-error at -1 {{argument to __builtin_ptrauth_schema_key parameter is not an authenticated value}}
+   __builtin_ptrauth_schema_is_address_discriminated(decltype(no_auth));
+   // expected-error at -1 {{argument to __builtin_ptrauth_schema_is_address_discriminated parameter is not an authenticated value}}
+   __builtin_ptrauth_schema_extra_discriminator(decltype(no_auth));
+   // expected-error at -1 {{argument to __builtin_ptrauth_schema_extra_discriminator parameter is not an authenticated value}}
+   __builtin_ptrauth_schema_options(decltype(no_auth));
+   // expected-error at -1 {{argument to __builtin_ptrauth_schema_options parameter is not an authenticated value}}
+
+
+   int *__ptrauth(1,0,1) no_addr_disc = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(decltype(no_addr_disc)));
+   _Static_assert(__builtin_ptrauth_schema_key(decltype(no_addr_disc)) == 1);
+   _Static_assert(!__builtin_ptrauth_schema_is_address_discriminated(decltype(no_addr_disc)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(decltype(no_addr_disc)) == 1);
+   int *__ptrauth(1,1,2) addr_disc = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(decltype(addr_disc)));
+   _Static_assert(__builtin_ptrauth_schema_key(decltype(addr_disc)) == 1);
+   _Static_assert(__builtin_ptrauth_schema_is_address_discriminated(decltype(addr_disc)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(decltype(addr_disc)) == 2);
+   int *__ptrauth(2,1,3) key2 = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(decltype(key2)));
+   _Static_assert(__builtin_ptrauth_schema_key(decltype(key2)) == 2);
+   _Static_assert(__builtin_ptrauth_schema_is_address_discriminated(decltype(key2)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(decltype(key2)) == 3);
+
+   int *__ptrauth(1, 0, 4, "strip") strip = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(decltype(strip)));
+   _Static_assert(__builtin_ptrauth_schema_key(decltype(strip)) == 1);
+   _Static_assert(!__builtin_ptrauth_schema_is_address_discriminated(decltype(strip)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(decltype(strip)) == 4);
+   _Static_assert(__builtin_strcmp("strip", __builtin_ptrauth_schema_options(decltype(strip))) == 0);
+
+   int *__ptrauth(1, 0, 4, __builtin_ptrauth_schema_options(decltype(strip))) strip2 = 0;
+   __auto_type test_ptr = &strip;
+   // Verify matching pointer auth schema
+   test_ptr = &strip2;
+
+   int *__ptrauth(1,0,5, "authenticates-null-values,sign-and-strip,isa-pointer") multi_option;
+   _Static_assert(__builtin_strcmp("sign-and-strip,isa-pointer,authenticates-null-values", __builtin_ptrauth_schema_options(decltype(multi_option))) == 0);
+   int *__ptrauth(1,0,5, "sign-and-strip,isa-pointer,authenticates-null-values") multi_option2;
+   _Static_assert(__builtin_strcmp(__builtin_ptrauth_schema_options(decltype(multi_option2)), __builtin_ptrauth_schema_options(decltype(multi_option))) == 0);
+
+
+   int *__ptrauth(1,0,5, "sign-and-auth") default_options;
+   _Static_assert(__builtin_strcmp("", __builtin_ptrauth_schema_options(decltype(default_options))) == 0);
+
+   void (*normal_func_ptr)(int) = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(decltype(normal_func_ptr)));
+   _Static_assert(__builtin_ptrauth_schema_key(decltype(normal_func_ptr)) == ptrauth_key_function_pointer);
+   _Static_assert(!__builtin_ptrauth_schema_is_address_discriminated(decltype(normal_func_ptr)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(decltype(normal_func_ptr)) == 0);
+   typedef void(*function_type)(int);
+   function_type __ptrauth(1,1,5) explicit_signed_func_ptr = 0;
+   _Static_assert(__builtin_ptrauth_has_authentication(decltype(explicit_signed_func_ptr)));
+   _Static_assert(__builtin_ptrauth_schema_key(decltype(explicit_signed_func_ptr)) == 1);
+   _Static_assert(__builtin_ptrauth_schema_is_address_discriminated(decltype(explicit_signed_func_ptr)));
+   _Static_assert(__builtin_ptrauth_schema_extra_discriminator(decltype(explicit_signed_func_ptr)) == 5);
+
+}
+
+template <typename T, bool has_ptrauth=__builtin_ptrauth_has_authentication(T)> struct PtrauthExtractor;
+template <typename T> struct PtrauthExtractor<T, false> {
+   static const bool isAuthenticated = false;
+   static const int key = -1;
+   static const int isAddressDiscriminated = false;
+   static const int extraDiscriminator = -1;
+   constexpr static auto options = "no-options";
+};
+
+template <typename T> struct PtrauthExtractor<T, true> {
+   static const bool isAuthenticated = true;
+   static const int key = __builtin_ptrauth_schema_key(T);
+   static const int isAddressDiscriminated = __builtin_ptrauth_schema_is_address_discriminated(T);
+   static const int extraDiscriminator = __builtin_ptrauth_schema_extra_discriminator(T);
+   constexpr static auto options = __builtin_ptrauth_schema_options(T);
+};
+
+template <typename T> void template_test() {
+   typedef PtrauthExtractor<T> TestStruct;
+   static_assert(__builtin_ptrauth_has_authentication(T) == TestStruct::isAuthenticated);
+   static_assert(__builtin_ptrauth_schema_key(T) == TestStruct::key);
+   // expected-error at -1 2 {{argument to __builtin_ptrauth_schema_key parameter is not an authenticated value}}
+   static_assert(__builtin_ptrauth_schema_is_address_discriminated(T) == TestStruct::isAddressDiscriminated);
+   static_assert(__builtin_ptrauth_schema_extra_discriminator(T) == TestStruct::extraDiscriminator);
+   static_assert(__builtin_strcmp(__builtin_ptrauth_schema_options(T), TestStruct::options) == 0);
+}
+
+void test_template_instantiator() {
+   template_test<int*>();
+   //expected-note at -1 {{in instantiation of function template specialization 'template_test<int *>' requested here}}
+   template_test<int* __ptrauth(1,1,1,"strip")>();
+   template_test<int* __ptrauth(2,1,2,"")>();
+   template_test<int* __ptrauth(3,0,3,"isa-pointer")>();
+   template_test<float>();
+   // expected-note at -1 {{in instantiation of function template specialization 'template_test<float>' requested here}}
+}
+



More information about the cfe-commits mailing list