[clang] [clang][ptrauth] add support for options parameter to __ptrauth (PR #136828)

Oliver Hunt via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 23 02:00:51 PDT 2025


================
@@ -8374,20 +8376,191 @@ static void HandlePtrAuthQualifier(ASTContext &Ctx, QualType &T,
   IsInvalid |= !S.checkPointerAuthDiscriminatorArg(
       ExtraDiscriminatorArg, Sema::PADAK_ExtraDiscPtrAuth, ExtraDiscriminator);
 
-  if (IsInvalid) {
-    Attr.setInvalid();
-    return;
+  std::optional<PointerAuthenticationMode> AuthenticationMode = std::nullopt;
+  SourceRange AuthenticationModeRange;
+
+  if (AuthenticationOptionsArg && !AuthenticationOptionsArg->containsErrors() ) {
+    std::string OptionsString;
+    bool IsInitialized = false;
+    const StringLiteral *OptionsStringLiteral = dyn_cast<StringLiteral>(AuthenticationOptionsArg);
+    auto ReportEvaluationOfExpressionIfNeeded = [&](){
+      if (OptionsStringLiteral || !IsInitialized)
+        return;
+      S.Diag(AuthenticationOptionsArg->getBeginLoc(),
+        diag::note_ptrauth_evaluating_options) << OptionsString << AuthenticationOptionsArg->getSourceRange();
+    };
+    auto DiagnoseInvalidOptionsParameter = [&](llvm::StringRef Reason, std::optional<char> InvalidCh, auto Location) {
+      S.Diag(AuthenticationOptionsArg->getExprLoc(),
+             diag::err_ptrauth_invalid_option)
+          << AttrName << Reason << Location << !!InvalidCh << (InvalidCh ? *InvalidCh : '\0');
+      Attr.setInvalid();
+      ReportEvaluationOfExpressionIfNeeded();
+    };
+    if (AuthenticationOptionsArg->isValueDependent() || AuthenticationOptionsArg->isTypeDependent()) {
+      DiagnoseInvalidOptionsParameter("is dependent", std::nullopt, AuthenticationOptionsArg->getSourceRange());
+      return;
+    }
+    if (OptionsStringLiteral) {
+      if (OptionsStringLiteral->containsNonAsciiOrNull()) {
+        DiagnoseInvalidOptionsParameter("contains invalid characters", std::nullopt, AuthenticationOptionsArg->getSourceRange());
+        return;
+      }
+      OptionsString = OptionsStringLiteral->getString();
+    } else if (S.EvaluateAsString(AuthenticationOptionsArg, OptionsString, S.Context, Sema::StringEvaluationContext::PtrauthOptions, /*ErrorOnInvalidMessage=*/false)) {
+      for (char Ch : OptionsString) {
+        if (!Ch || !isascii(Ch)) {
+          DiagnoseInvalidOptionsParameter("contains invalid characters", Ch, AuthenticationOptionsArg->getSourceRange());
+          return;
+        }
+      }
+    } else {
+      Attr.setInvalid();
+      return;
+    }
+    IsInitialized = true;
+    bool HasSeenOption = false;
+    unsigned CurrentIdx = 0;
+
+    auto OptionStringIdxLocation = [&](unsigned Idx) {
+      if (OptionsStringLiteral)
+        return OptionsStringLiteral->getLocationOfByte(Idx, Ctx.getSourceManager(), Ctx.getLangOpts(), Ctx.getTargetInfo());
+      return AuthenticationOptionsArg->getBeginLoc();
+    };
+    auto OptionStringRange = [&](unsigned StartIdx, unsigned EndIdx) {
+      if (!OptionsStringLiteral)
+        return AuthenticationOptionsArg->getSourceRange();
+      return SourceRange(OptionStringIdxLocation(StartIdx),
+                        OptionStringIdxLocation(EndIdx));
+    };
+    auto NextOption = [&]() -> std::optional<std::pair<unsigned, unsigned>> {
+      auto ConsumeChar = [&](auto Filter) -> char {
+        if (CurrentIdx >= OptionsString.size())
+          return 0;
+        char Current = OptionsString[CurrentIdx];
+        if (!Filter(Current))
+          return 0;
+        ++CurrentIdx;
+        return Current;
+      };
+      auto SkipWhiteSpace = [&]() {
+        while (ConsumeChar(isWhitespace)) {
+          // this space is intentionally left blank
+        }
+      };
+      auto MatchCharacter = [](char MatchChar) {
+        return [MatchChar](char Ch){ return MatchChar == Ch; };
+      };
+      SkipWhiteSpace();
+      if (CurrentIdx == OptionsString.size())
+        return std::nullopt;
+      if (HasSeenOption) {
+        if (!ConsumeChar(MatchCharacter(','))) {
+          SourceLocation ErrorLocation = OptionStringIdxLocation(CurrentIdx);
+          S.Diag(ErrorLocation, diag::err_ptrauth_option_missing_comma)
+            << AttrName << ErrorLocation;
+          ReportEvaluationOfExpressionIfNeeded();
+          return std::nullopt;
+        }
+        SkipWhiteSpace();
+      }
+      HasSeenOption = true;
+      if (CurrentIdx == OptionsString.size()) {
+        SourceLocation ErrorLocation = OptionStringIdxLocation(CurrentIdx);
+        S.Diag(ErrorLocation, diag::err_ptrauth_unexpected_option_end)
+            << AttrName << ErrorLocation;
+        ReportEvaluationOfExpressionIfNeeded();
+      }
+      unsigned OptionStartIdx = CurrentIdx;
+      while (ConsumeChar(isalpha) || ConsumeChar(MatchCharacter('-'))) {
+        // this space is intentionally left blank
+      }
+      unsigned OptionEndIdx = CurrentIdx;
+      if (OptionStartIdx == OptionEndIdx) {
+        StringRef ErrorString(&OptionsString[CurrentIdx], 1);
+        SourceLocation ErrorLocation = OptionStringIdxLocation(OptionStartIdx);
+        S.Diag(ErrorLocation, diag::err_ptrauth_option_unexpected_token) << ErrorString << AttrName << ErrorLocation;
+        ReportEvaluationOfExpressionIfNeeded();
+        IsInvalid = true;
+        return std::nullopt;
+      }
+      return std::make_pair(OptionStartIdx, OptionEndIdx);
+    };
+
+    auto OptionHandler = [&](StringRef TokenStr, SourceRange TokenRange,
----------------
ojhunt wrote:

This is more generic than is strictly needed at this point, because future PRs add additional option types and flags

https://github.com/llvm/llvm-project/pull/136828


More information about the cfe-commits mailing list