https://github.com/ojhunt updated https://github.com/llvm/llvm-project/pull/136828
>From aa0820410e85eb8ca28328bda5e95d65343dd400 Mon Sep 17 00:00:00 2001 From: Oliver Hunt <[email protected]> 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" These modes exist to support ABI migration, and should not be used beyond these specific use cases. 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@-1 {{unknown '__ptrauth' authentication option 'Foo'}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip", 41) invalid14_1; +// expected-error@-1 {{'__ptrauth' qualifier must take between 1 and 4 arguments}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,sign-and-strip") invalid15; +// expected-error@-1 {{repeated '__ptrauth' authentication mode 'sign-and-strip', prior mode was 'strip'}} +// expected-note@-2 {{previous option specified here}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "isa-pointer,isa-pointer") invalid16; +// expected-error@-1 {{repeated '__ptrauth' authentication option}} +// expected-note@-2 {{previous option specified here}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip, , isa-pointer") invalid18; +// expected-error@-1 {{unexpected comma in '__ptrauth' options argument}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip,") invalid19; +// expected-error@-1 {{unexpected trailing comma in '__ptrauth' options argument}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",") invalid20; +// expected-error@-1 {{unexpected trailing comma in '__ptrauth' options argument}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, ",,") invalid21; +// expected-error@-1 {{unexpected comma in '__ptrauth' options argument}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip isa-pointer") invalid22; +// expected-error@-1 {{expected a comma before 'isa-pointer' in '__ptrauth' options argument}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip\nisa-pointer") invalid23; +// expected-error@-1 {{expected a comma before 'isa-pointer' in '__ptrauth' options argument}} +int *__ptrauth(VALID_DATA_KEY, 1, 65535, "strip" + " isa-pointer") invalid24; +// expected-error@-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@-1 {{unexpected comma in '__ptrauth' options argument}} +int *__ptrauth(VALID_DATA_KEY, 1, 0, "sign-and-strip,\t,isa-pointer") invalid26; +// expected-error@-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@-1 {{int *__ptrauth(1,1,0,"strip") *}} +int *__ptrauth(1, 1, 0, "sign-and-strip") * signAndStrip = &global_ptr; +// expected-error@-1 {{int *__ptrauth(1,1,0,"sign-and-strip") *}} +int *__ptrauth(1, 1, 0, "sign-and-auth") * signAndAuth = &global_ptr; +// expected-error@-1 {{int *__ptrauth(1,1,0) *}} +int *__ptrauth(1, 1, 0, "sign-and-auth, isa-pointer") * signAndAuthIsa = &global_ptr; +// expected-error@-1 {{int *__ptrauth(1,1,0,"isa-pointer") *}} +int *__ptrauth(1, 1, 0, "sign-and-strip, isa-pointer") * signAndStripIsa = &global_ptr; +// expected-error@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-1 {{unexpected trailing comma in '__ptrauth' options argument}} + // expected-note@-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@-1 {{expected a comma before 'characters' in '__ptrauth' options argument}} + // expected-note@-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@-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@-1 {{unknown '__ptrauth' authentication option 'trip'}} + // expected-note@-2 {{options parameter evaluated to 'trip'}} + + // Handle evaluation failing + int * __ptrauth(1,1,1,const_options<StringType>(50)) fifty; + // expected-error@-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@-1 {{unexpected trailing comma in '__ptrauth' options argument}} + // expected-note@-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@-1 {{expected a comma before 'characters' in '__ptrauth' options argument}} + // expected-note@-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@-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@-1 {{unknown '__ptrauth' authentication option 'trip'}} + // expected-note@-2 {{options parameter evaluated to 'trip'}} + + // Handle evaluation failing + int * __ptrauth(1,1,1,const_options<StringType>(50)) fifty; + // expected-error@-1 {{the expression in '__ptrauth' options must be a string literal or an object with 'data()' and 'size()' member functions}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
