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 <oli...@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@-1 {{argument to __builtin_ptrauth_schema_key parameter is not an authenticated value}} + __builtin_ptrauth_schema_is_address_discriminated(typeof(no_auth)); + // expected-error@-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@-1 {{argument to __builtin_ptrauth_schema_extra_discriminator parameter is not an authenticated value}} + __builtin_ptrauth_schema_options(typeof(no_auth)); + // expected-error@-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@-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@-1 {{argument to __builtin_ptrauth_schema_key parameter is not an authenticated value}} + __builtin_ptrauth_schema_is_address_discriminated(decltype(no_auth)); + // expected-error@-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@-1 {{argument to __builtin_ptrauth_schema_extra_discriminator parameter is not an authenticated value}} + __builtin_ptrauth_schema_options(decltype(no_auth)); + // expected-error@-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@-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@-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@-1 {{in instantiation of function template specialization 'template_test<float>' requested here}} +} + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits