llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Oliver Hunt (ojhunt) <details> <summary>Changes</summary> This PR adds support for an 'options' parameter for the __ptrauth qualifier. The initial version only exposes the authehntication modes: * "strip" * "sign-and-strip" * "sign-and-auth" We also support parsing the options but not yet the implementation * "isa-pointer" * "authenticates-null-values" The initial support for authentication mode controls exist to support ABI changes over time, and as a byproduct support basic initial tests for option parsing. --- Patch is 51.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/172187.diff 14 Files Affected: - (modified) clang/docs/PointerAuthentication.rst (+28-4) - (modified) clang/include/clang/Basic/Attr.td (+3-3) - (modified) clang/include/clang/Basic/DiagnosticParseKinds.td (+1-1) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+12) - (modified) clang/include/clang/Basic/LangOptions.h (+11) - (modified) clang/lib/AST/TypePrinter.cpp (+33-1) - (modified) clang/lib/CodeGen/CGExprConstant.cpp (+17-7) - (modified) clang/lib/Parse/ParseDecl.cpp (+1-1) - (modified) clang/lib/Sema/SemaType.cpp (+147-9) - (added) clang/test/CodeGen/ptrauth-stripping.c (+327) - (modified) clang/test/Parser/ptrauth-qualifier.c (+1-1) - (added) clang/test/Sema/ptrauth-qualifier-options.c (+108) - (modified) clang/test/Sema/ptrauth-qualifier.c (+33-6) - (added) clang/test/SemaCXX/ptrauth-qualifier-constexpr-options.cpp (+65) ``````````diff 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 0b006142cbb74..d0d65fbe166f0 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3750,9 +3750,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 442a90ec2472d..6abadc6d4655e 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1771,7 +1771,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 381d1fb063eba..c285e2d1e2a0d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1048,6 +1048,18 @@ def err_ptrauth_extra_discriminator_invalid : Error< "invalid extra discriminator flag '%0'; '__ptrauth' requires a value between " "'0' and '%1'">; +// __ptrauth qualifier options string +def note_ptrauth_evaluating_options + : Note<"options parameter evaluated to '%0'">; +def err_ptrauth_invalid_option : Error<"'__ptrauth' options parameter %0">; +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%select{, prior " + "mode was '%2'| '%1'}0">; +def err_ptrauth_option_missing_comma + : Error<"missing comma after '%0' option in '__ptrauth' qualifier">; + /// main() // static main() is not an error in C, just in C++. def warn_static_main : Warning<"'main' should not be declared static">, diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 4fa2dcffc75b6..0d1d94d63d076 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -67,6 +67,17 @@ enum class PointerAuthenticationMode : unsigned { SignAndAuth }; +static constexpr llvm::StringLiteral PointerAuthenticationOptionStrip = "strip"; +static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndStrip = + "sign-and-strip"; +static constexpr llvm::StringLiteral PointerAuthenticationOptionSignAndAuth = + "sign-and-auth"; +static constexpr llvm::StringLiteral PointerAuthenticationOptionIsaPointer = + "isa-pointer"; +static constexpr llvm::StringLiteral + PointerAuthenticationOptionAuthenticatesNullValues = + "authenticates-null-values"; + /// Bitfields of LangOptions, split out from LangOptions in order to ensure that /// this large collection of bitfields is a trivial class type. class LangOptionsBase { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index d2881d5ac518a..030168849c7e2 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2610,7 +2610,39 @@ void PointerAuthQualifier::print(raw_ostream &OS, OS << "__ptrauth("; OS << getKey(); OS << "," << unsigned(isAddressDiscriminated()) << "," - << getExtraDiscriminator() << ")"; + << getExtraDiscriminator(); + + bool HasAppendedOption = false; + auto AppendOption = [&](StringRef Option) { + OS << ","; + if (!HasAppendedOption) + OS << '"'; + HasAppendedOption = true; + OS << Option; + }; + switch (getAuthenticationMode()) { + case PointerAuthenticationMode::None: + llvm_unreachable("Mode is unauthenticated but claims to be present"); + return; + case PointerAuthenticationMode::Strip: + AppendOption(PointerAuthenticationOptionStrip); + break; + case PointerAuthenticationMode::SignAndStrip: + AppendOption(PointerAuthenticationOptionSignAndStrip); + break; + case clang::PointerAuthenticationMode::SignAndAuth: + // Don't emit default auth + break; + } + if (isIsaPointer()) + AppendOption(PointerAuthenticationOptionIsaPointer); + if (authenticatesNullValues()) + AppendOption(PointerAuthenticationOptionAuthenticatesNullValues); + + if (HasAppendedOption) + OS << '"'; + + OS << ")"; } std::string Qualifiers::getAsString() const { diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index 0eec4dba4824a..15ae2bf6f28c6 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -2129,6 +2129,13 @@ class ConstantLValueEmitter : public ConstStmtVisitor<ConstantLValueEmitter, } +static bool shouldSignPointer(const PointerAuthQualifier &PointerAuth) { + PointerAuthenticationMode AuthenticationMode = + PointerAuth.getAuthenticationMode(); + return AuthenticationMode == PointerAuthenticationMode::SignAndStrip || + AuthenticationMode == PointerAuthenticationMode::SignAndAuth; +} + llvm::Constant *ConstantLValueEmitter::tryEmit() { const APValue::LValueBase &base = Value.getLValueBase(); @@ -2162,7 +2169,8 @@ llvm::Constant *ConstantLValueEmitter::tryEmit() { // Apply pointer-auth signing from the destination type. if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth(); - PointerAuth && !result.HasDestPointerAuth) { + PointerAuth && !result.HasDestPointerAuth && + shouldSignPointer(PointerAuth)) { value = Emitter.tryEmitConstantSignedPointer(value, PointerAuth); if (!value) return nullptr; @@ -2210,8 +2218,9 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { if (D->hasAttr<WeakRefAttr>()) return CGM.GetWeakRefReference(D).getPointer(); - auto PtrAuthSign = [&](llvm::Constant *C) { - if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth()) { + auto PtrAuthSign = [&](llvm::Constant *C, bool IsFunction) { + if (PointerAuthQualifier PointerAuth = DestType.getPointerAuth(); + PointerAuth && shouldSignPointer(PointerAuth)) { C = applyOffset(C); C = Emitter.tryEmitConstantSignedPointer(C, PointerAuth); return ConstantLValue(C, /*applied offset*/ true, /*signed*/ true); @@ -2219,7 +2228,7 @@ ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) { CGPointerAuthInfo AuthInfo; - if (EnablePtrAuthFunctionTypeDiscrimination) + if (IsFunction && EnablePtrAuthFunctionTypeDiscrimination) AuthInfo = CGM.getFunctionPointerAuthInfo(DestType); if (AuthInfo) { @@ -2240,18 +2249,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 8688ccf41acb5..270c039dc6f40 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3172,7 +3172,7 @@ void Parser::ParsePtrauthQualifier(ParsedAttributes &Attrs) { T.consumeClose(); SourceLocation EndLoc = T.getCloseLocation(); - if (ArgExprs.empty() || ArgExprs.size() > 3) { + if (ArgExprs.empty() || ArgExprs.size() > 4) { Diag(KwLoc, diag::err_ptrauth_qualifier_bad_arg_count); return; } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index fd64d4456cbfa..880a30ea55b54 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -8456,14 +8456,15 @@ static void HandleNeonVectorTypeAttr(QualType &CurType, const ParsedAttr &Attr, /// Handle the __ptrauth qualifier. static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T, const ParsedAttr &Attr, Sema &S) { - - assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 3) && - "__ptrauth qualifier takes between 1 and 3 arguments"); + assert((Attr.getNumArgs() > 0 && Attr.getNumArgs() <= 4) && + "__ptrauth qualifier takes between 1 and 4 arguments"); 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)) { @@ -8479,10 +8480,138 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T, IsAddressDiscriminated); IsInvalid |= !S.checkPointerAuthDiscriminatorArg( ExtraDiscriminatorArg, PointerAuthDiscArgKind::Extra, ExtraDiscriminator); + std::string LastAuthenticationMode; + std::optional<PointerAuthenticationMode> AuthenticationMode = std::nullopt; + bool IsIsaPointer = false; + bool AuthenticatesNullValues = false; + + if (AuthenticationOptionsArg && !AuthenticationOptionsArg->containsErrors()) { + StringRef OptionsString; + std::string EvaluatedString; + bool HasEvaluatedOptionsString = false; + const StringLiteral *OptionsStringLiteral = + dyn_cast<StringLiteral>(AuthenticationOptionsArg); + SourceRange AuthenticationOptionsRange = + AuthenticationOptionsArg->getSourceRange(); + bool ReportedEvaluation = false; + auto ReportEvaluationOfExpressionIfNeeded = [&]() { + if (OptionsStringLiteral || !HasEvaluatedOptionsString || + ReportedEvaluation) + return; + ReportedEvaluation = true; + S.Diag(AuthenticationOptionsRange.getBegin(), + diag::note_ptrauth_evaluating_options) + << OptionsString << AuthenticationOptionsRange; + }; + auto DiagnoseInvalidOptionsParameter = [&](llvm::StringRef Reason) { + S.Diag(AuthenticationOptionsRange.getBegin(), + diag::err_ptrauth_invalid_option) + << Reason; + Attr.setInvalid(); + IsInvalid = true; + ReportEvaluationOfExpressionIfNeeded(); + }; + if (AuthenticationOptionsArg->isValueDependent() || + AuthenticationOptionsArg->isTypeDependent()) { + DiagnoseInvalidOptionsParameter("is dependent"); + return; + } + if (OptionsStringLiteral) { + OptionsString = OptionsStringLiteral->getString(); + HasEvaluatedOptionsString = true; + } else { + Expr::EvalResult Eval; + bool Result = AuthenticationOptionsArg->EvaluateAsRValue(Eval, Ctx); + if (Result && Eval.Val.isLValue()) { + auto *BaseExpr = Eval.Val.getLValueBase().dyn_cast<const Expr *>(); + const StringLiteral *EvaluatedStringLiteral = + dyn_cast<StringLiteral>(const_cast<Expr *>(BaseExpr)); + if (EvaluatedStringLiteral) { + CharUnits StartOffset = Eval.Val.getLValueOffset(); + EvaluatedString = EvaluatedStringLiteral->getString().drop_front( + StartOffset.getQuantity()); + OptionsString = EvaluatedString; + HasEvaluatedOptionsString = true; + } + } + } + if (!HasEvaluatedOptionsString) { + DiagnoseInvalidOptionsParameter( + "must be a string of comma separated flags"); + return; + } + for (char Ch : OptionsString) { + if (Ch != '-' && Ch != ',' && !isWhitespace(Ch) && !isalpha(Ch)) { + DiagnoseInvalidOptionsParameter("contains invalid characters"); + return; + } + } + HasEvaluatedOptionsString = true; + OptionsString = OptionsString.trim(); + llvm::SmallVector<StringRef> Options; + if (!OptionsString.empty()) + OptionsString.split(Options, ','); + + auto OptionHandler = [&](auto Value, auto *Option, + std::string *LastOption = nullptr) { + return [&, Value, Option, LastOption](StringRef OptionString) { + if (!*Option) { + *Option = Value; + if (LastOption) + *LastOption = OptionString; + return true; + } + bool IsAuthenticationMode = + std::is_same_v<decltype(Value), PointerAuthenticationMode>; + S.Diag(AuthenticationOptionsRange.getBegin(), + diag::err_ptrauth_repeated_authentication_option) + << !IsAuthenticationMode << OptionString + << (LastOption ? *LastOption : ""); + return false; + }; + }; - if (IsInvalid) { - Attr.setInvalid(); - return; + for (unsigned Idx = 0; Idx < Options.size(); ++Idx) { + StringRef Option = Options[Idx].trim(); + if (Option.empty()) { + bool IsLastOption = Idx == (Options.size() - 1); + DiagnoseInvalidOptionsParameter( + IsLastOption ? "has a trailing comma" : "contains an empty option"); + continue; + } + auto SelectedHandler = + llvm::StringSwitch<std::function<bool(StringRef)>>(Option) + .Case(PointerAuthenticationOptionStrip, + OptionHandler(PointerAuthenticationMode::Strip, + &AuthenticationMode, &LastAuthenticationMode)) + .Case(PointerAuthenticationOptionSignAndStrip, + OptionHandler(PointerAuthenticationMode::SignAndStrip, + &AuthenticationMode, &LastAuthenticationMode)) + .Case(PointerAuthenticationOptionSignAndAuth, + OptionHandler(PointerAuthenticationMode::SignAndAuth, + &AuthenticationMode, &LastAuthenticationMode)) + .Case(PointerAuthenticationOptionIsaPointer, + OptionHandler(true, &IsIsaPointer)) + .Case(PointerAuthenticationOptionAuthenticatesNullValues, + OptionHandler(true, &AuthenticatesNullValues)) + .Default([&](StringRef Option) { + if (size_t WhitespaceIndex = + Option.find_first_of(" \t\n\v\f\r"); + WhitespaceIndex != Option.npos) { + StringRef LeadingOption = Option.slice(0, WhitespaceIndex); + S.Diag(AuthenticationOptionsRange.getBegin(), + diag::err_ptrauth_option_missing_comma) + << LeadingOption; + } else { + S.Diag(AuthenticationOptionsRange.getBegin(), + diag::err_ptrauth_unknown_authentication_option) + << Option; + } + return false; + }); + if (!SelectedHandler(Option)) + IsInvalid = true; + } } if (!T->isSignableType(Ctx) && !T->isDependentType()) { @@ -8491,6 +8620,9 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T, return; } + if (!AuthenticationMode) + AuthenticationMode = PointerAuthenticationMode::SignAndAuth; + if (T.getPointerAuth()) { S.Diag(Attr.getLoc(), diag::err_ptrauth_qualifier_redundant) << T; Attr.setInvalid(); @@ -8503,13 +8635,19 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T, return; } + if (IsInvalid) { + Attr.setInvalid(); + return; + } + assert((!IsAddressDiscriminatedArg || IsAddressDiscriminated <= 1) && "address discriminator arg should be either 0 or 1"); PointerAuthQualifier Qual = PointerAuthQualifier::Create( - Key, IsAddressDiscriminated, ExtraDiscriminator, - PointerAuthenticationMode::SignAndAuth, /*IsIsaPointer=*/false, - /*AuthenticatesNullValues=*/false); + Key, IsAddressDiscriminated, ExtraDiscriminator, *AuthenticationMode, + IsIsaPointer, AuthenticatesNullValues); + assert(Qual.getAuthenticationMode() == *AuthenticationMode); T = S.Context.getPointerAuthType(T, Qual); + assert(T.getPointerAuth().getAuthenticationMode() == *AuthenticationMode); } /// HandleArmSveVectorBitsTypeAttr - The "arm_sve_vector_bits... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/172187 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
