[clang] [Clang][AArch64] Command-line options for A-profile's Sign Return Address Hardening (PR #176171)

Victor Campos via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 16 05:07:41 PDT 2026


https://github.com/vhscampos updated https://github.com/llvm/llvm-project/pull/176171

>From 4f775ae42fbc2729697b32ed5b5310a86f9cbfc4 Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Tue, 13 Jan 2026 20:39:02 +0000
Subject: [PATCH 1/2] [Clang][AArch64] Command-line options for A-profile's
 Sign Return Address Hardening

This patch introduces a new command-line option to enable the AArch64
A-profile's Sign Return Address Hardening. It also introduces a new
function attribute with the same naming as the new command-line option.

At the time of this patch, this new option enables the hardening against
the PACMAN attack [1] using a load of the return address [2].

The new option, -mharden-pac-ret, can take one of two values:

 - none: disable hardening. (The default if the option is absent)
 - load-return-address: enables hardening using the mitigation based on
   load of return address.

The corresponding function attribute takes the option and its possible
values using the same naming.

1: https://pacmanattack.com
2: https://developer.arm.com/documentation/101754/0624/armclang-Reference/armclang-Command-line-Options/-mharden-pac-ret
---
 .../clang/Basic/DiagnosticDriverKinds.td      |  2 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  5 +++
 clang/include/clang/Basic/LangOptions.def     |  2 +
 clang/include/clang/Basic/LangOptions.h       | 12 ++++++
 clang/include/clang/Basic/TargetInfo.h        | 22 ++++++++++
 clang/include/clang/Options/Options.td        |  9 ++++
 clang/lib/Basic/Targets/AArch64.cpp           | 16 +++++++
 clang/lib/Basic/Targets/AArch64.h             |  2 +
 clang/lib/CodeGen/TargetInfo.cpp              |  4 ++
 clang/lib/CodeGen/Targets/AArch64.cpp         |  3 ++
 clang/lib/Driver/ToolChains/Clang.cpp         | 22 +++++++++-
 clang/lib/Sema/SemaDeclAttr.cpp               | 18 +++++++-
 .../aarch64-sign-return-address-harden.c      | 13 ++++++
 clang/test/Driver/aarch64-security-options.c  | 30 +++++++++++++
 clang/test/Driver/arm-security-options.c      | 19 ++++++++
 .../Frontend/aarch64-harden-pac-ret-err.c     |  3 ++
 .../aarch64-harden-pac-ret-attr-err-warn.c    | 21 +++++++++
 clang/test/Sema/aarch64-harden-pac-ret-attr.c | 43 +++++++++++++++++++
 18 files changed, 244 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGen/aarch64-sign-return-address-harden.c
 create mode 100644 clang/test/Frontend/aarch64-harden-pac-ret-err.c
 create mode 100644 clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
 create mode 100644 clang/test/Sema/aarch64-harden-pac-ret-attr.c

diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index db0f521b73544..d16fbd45f104e 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -544,6 +544,8 @@ def warn_ignoring_verify_debuginfo_preserve_export : Warning<
   InGroup<UnusedCommandLineArgument>;
 def warn_unsupported_branch_protection: Warning <
   "invalid branch protection option '%0' in '%1'">, InGroup<BranchProtection>;
+def warn_harden_pac_ret_requires_pac_ret: Warning<
+  "ignoring '-mharden-pac-ret' as it requires return address signing">, InGroup<UnusedCommandLineArgument>;
 def err_sls_hardening_arm_not_supported : Error<
   "-mharden-sls is only supported on armv7-a or later">;
 def warn_drv_large_data_threshold_invalid_code_model: Warning<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5cbbc7d130c99..fdfe7792441b9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3384,6 +3384,11 @@ def err_invalid_branch_protection_spec : Error<
   "invalid or misplaced branch protection specification '%0'">;
 def warn_unsupported_branch_protection_spec : Warning<
   "unsupported branch protection specification '%0'">, InGroup<BranchProtection>;
+def warn_attribute_harden_pac_ret_requires_pac_ret: Warning<
+  "'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored">,
+  InGroup<IgnoredAttributes>;
+def err_invalid_harden_pac_ret_spec : Error<
+  "invalid or misplaced pac-ret hardening specification '%0'">;
 def err_attribute_invalid_atomic_argument : Error<
   "invalid argument '%0' to atomic attribute; valid options are: "
   "'remote_memory', 'fine_grained_memory', 'ignore_denormal_mode' (optionally "
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 8cba1dbaee24e..f3c5613d3dab7 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -454,6 +454,8 @@ ENUM_LANGOPT(SignReturnAddressScope, SignReturnAddressScopeKind, 2, SignReturnAd
              "Scope of return address signing")
 ENUM_LANGOPT(SignReturnAddressKey, SignReturnAddressKeyKind, 1, SignReturnAddressKeyKind::AKey, NotCompatible,
              "Key used for return address signing")
+ENUM_LANGOPT(SignReturnAddressHardening, SignReturnAddressHardeningKind, 1,
+             SignReturnAddressHardeningKind::None, NotCompatible, "Hardening of return address signing")
 LANGOPT(BranchTargetEnforcement, 1, 0, NotCompatible, "Branch-target enforcement enabled")
 LANGOPT(BranchProtectionPAuthLR, 1, 0, NotCompatible, "Use PC as a diversifier using PAuthLR NOP instructions.")
 LANGOPT(GuardedControlStack, 1, 0, NotCompatible, "Guarded control stack enabled")
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index ebd0436fa154b..c0e82b3edd2cb 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -299,6 +299,13 @@ class LangOptionsBase {
     BKey
   };
 
+  enum class SignReturnAddressHardeningKind {
+    /// Regular return address signing.
+    None,
+    /// Hardened return address signing with load of return address.
+    LoadReturnAddress
+  };
+
   enum class ThreadModelKind {
     /// POSIX Threads.
     POSIX,
@@ -728,6 +735,11 @@ class LangOptions : public LangOptionsBase {
     return getSignReturnAddressScope() == SignReturnAddressScopeKind::All;
   }
 
+  bool hasSignReturnAddressHardening() const {
+    return getSignReturnAddressHardening() !=
+           SignReturnAddressHardeningKind::None;
+  }
+
   bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; }
 
   bool hasDefaultVisibilityExportMapping() const {
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 83467b8e93b6a..10779518275b4 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -62,10 +62,12 @@ struct ParsedTargetAttr {
   StringRef CPU;
   StringRef Tune;
   StringRef BranchProtection;
+  StringRef SignReturnAddrHardening;
   StringRef Duplicate;
   bool operator ==(const ParsedTargetAttr &Other) const {
     return Duplicate == Other.Duplicate && CPU == Other.CPU &&
            Tune == Other.Tune && BranchProtection == Other.BranchProtection &&
+           SignReturnAddrHardening == Other.SignReturnAddrHardening &&
            Features == Other.Features;
   }
 };
@@ -1471,6 +1473,7 @@ class TargetInfo : public TransferrableTargetInfo,
   public:
     LangOptions::SignReturnAddressScopeKind SignReturnAddr;
     LangOptions::SignReturnAddressKeyKind SignKey;
+    LangOptions::SignReturnAddressHardeningKind SignReturnAddressHardening;
     bool BranchTargetEnforcement;
     bool BranchProtectionPAuthLR;
     bool GuardedControlStack;
@@ -1497,9 +1500,21 @@ class TargetInfo : public TransferrableTargetInfo,
       llvm_unreachable("Unexpected SignReturnAddressKeyKind");
     }
 
+    const char *getSignReturnAddressHardeningStr() const {
+      switch (SignReturnAddressHardening) {
+      case LangOptions::SignReturnAddressHardeningKind::None:
+        return "none";
+      case LangOptions::SignReturnAddressHardeningKind::LoadReturnAddress:
+        return "load-return-address";
+      }
+      llvm_unreachable("Unexpected SignReturnAddressHardeningKind");
+    }
+
     BranchProtectionInfo()
         : SignReturnAddr(LangOptions::SignReturnAddressScopeKind::None),
           SignKey(LangOptions::SignReturnAddressKeyKind::AKey),
+          SignReturnAddressHardening(
+              LangOptions::SignReturnAddressHardeningKind::None),
           BranchTargetEnforcement(false), BranchProtectionPAuthLR(false),
           GuardedControlStack(false) {}
 
@@ -1513,6 +1528,7 @@ class TargetInfo : public TransferrableTargetInfo,
       SignKey = LangOpts.isSignReturnAddressWithAKey()
                     ? LangOptions::SignReturnAddressKeyKind::AKey
                     : LangOptions::SignReturnAddressKeyKind::BKey;
+      SignReturnAddressHardening = LangOpts.getSignReturnAddressHardening();
       BranchTargetEnforcement = LangOpts.BranchTargetEnforcement;
       BranchProtectionPAuthLR = LangOpts.BranchProtectionPAuthLR;
       GuardedControlStack = LangOpts.GuardedControlStack;
@@ -1535,6 +1551,12 @@ class TargetInfo : public TransferrableTargetInfo,
     return false;
   }
 
+  /// Validate the Return Address Signing Hardening specification
+  virtual std::optional<LangOptions::SignReturnAddressHardeningKind>
+  validateSignReturnAddressHardening(StringRef Spec) const {
+    return std::nullopt;
+  }
+
   /// Perform initialization based on the user configured
   /// set of features (e.g., +sse4).
   ///
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 2f57a5b13b917..3ac001d05be82 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -5521,6 +5521,15 @@ def msign_return_address_EQ : Joined<["-"], "msign-return-address=">,
   Visibility<[ClangOption, CC1Option]>,
   Group<m_Group>, Values<"none,all,non-leaf">,
   HelpText<"Select return address signing scope">;
+
+def mharden_pac_ret_EQ : Joined<["-"], "mharden-pac-ret=">,
+  Visibility<[ClangOption, CC1Option]>,
+  Flags<[TargetSpecific]>, Group<m_Group>,
+  HelpText<"Select the return address signing hardening scheme. <arg> must be: none, load-return-address">,
+  Values<"none,load-return-address">, NormalizedValues<["None", "LoadReturnAddress"]>,
+  NormalizedValuesScope<"LangOptions::SignReturnAddressHardeningKind">,
+  MarshallingInfoEnum<LangOpts<"SignReturnAddressHardening">, "None">;
+
 let Flags = [TargetSpecific] in {
 def mbranch_protection_EQ : Joined<["-"], "mbranch-protection=">,
   Group<m_Group>,
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index fe407e9fc1789..639540fc13d87 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -276,6 +276,17 @@ bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
   return true;
 }
 
+std::optional<LangOptions::SignReturnAddressHardeningKind>
+AArch64TargetInfo::validateSignReturnAddressHardening(StringRef Spec) const {
+  assert(!Spec.empty() && "Spec must not be empty");
+  return llvm::StringSwitch<
+             std::optional<LangOptions::SignReturnAddressHardeningKind>>(Spec)
+      .Case("load-return-address",
+            LangOptions::SignReturnAddressHardeningKind::LoadReturnAddress)
+      .Case("none", LangOptions::SignReturnAddressHardeningKind::None)
+      .Default(std::nullopt);
+}
+
 bool AArch64TargetInfo::isValidCPUName(StringRef Name) const {
   return llvm::AArch64::parseCpu(Name).has_value();
 }
@@ -1290,6 +1301,11 @@ ParsedTargetAttr AArch64TargetInfo::parseTargetAttr(StringRef Features) const {
       continue;
     }
 
+    if (Feature.starts_with("harden-pac-ret=")) {
+      Ret.SignReturnAddrHardening = Feature.split('=').second.trim();
+      continue;
+    }
+
     if (Feature.starts_with("arch=")) {
       if (FoundArch)
         Ret.Duplicate = "arch=";
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 2d3b8d2a8d950..c0e52319ba628 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -147,6 +147,8 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
                                 BranchProtectionInfo &BPI,
                                 const LangOptions &LO,
                                 StringRef &Err) const override;
+  std::optional<LangOptions::SignReturnAddressHardeningKind>
+  validateSignReturnAddressHardening(StringRef Spec) const override;
 
   bool isValidCPUName(StringRef Name) const override;
   void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 342a3af0ac1ee..530304f9530ad 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -233,6 +233,10 @@ void TargetCodeGenInfo::setBranchProtectionFnAttributes(
   if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) {
     F.addFnAttr("sign-return-address", BPI.getSignReturnAddrStr());
     F.addFnAttr("sign-return-address-key", BPI.getSignKeyStr());
+    if (BPI.SignReturnAddressHardening !=
+        LangOptions::SignReturnAddressHardeningKind::None)
+      F.addFnAttr("sign-return-address-harden",
+                  BPI.getSignReturnAddressHardeningStr());
   } else {
     if (F.hasFnAttribute("sign-return-address"))
       F.removeFnAttr("sign-return-address");
diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp
index 963b74927036a..2d197e27dc115 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -160,6 +160,9 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {
             Attr.BranchProtection, Attr.CPU, BPI, CGM.getLangOpts(), Error);
         assert(Error.empty());
       }
+      if (!Attr.SignReturnAddrHardening.empty())
+        Fn->addFnAttr("sign-return-address-harden",
+                      Attr.SignReturnAddrHardening);
     }
     setBranchProtectionFnAttributes(BPI, *Fn);
     setPointerAuthFnAttributes(CGM.getCodeGenOpts().PointerAuth, *Fn);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 4ca98600d6e93..f91e1fd367043 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1378,6 +1378,19 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
                      ? Args.getLastArg(options::OPT_msign_return_address_EQ,
                                        options::OPT_mbranch_protection_EQ)
                      : Args.getLastArg(options::OPT_mbranch_protection_EQ);
+  const Arg *HardenPACRetArg = Args.getLastArg(options::OPT_mharden_pac_ret_EQ);
+  const Driver &D = TC.getDriver();
+
+  if (HardenPACRetArg) {
+    if (!isAArch64) {
+      D.Diag(diag::err_drv_unsupported_opt_for_target)
+          << HardenPACRetArg->getSpelling() << TC.getTriple().str();
+      return;
+    }
+    if (!A)
+      D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
+  }
+
   if (!A) {
     if (Triple.isOSOpenBSD() && isAArch64) {
       CmdArgs.push_back("-msign-return-address=non-leaf");
@@ -1387,7 +1400,6 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
     return;
   }
 
-  const Driver &D = TC.getDriver();
   if (!(isAArch64 || (Triple.isArmT32() && Triple.isArmMClass())))
     D.Diag(diag::warn_incompatible_branch_protection_option)
         << Triple.getArchName();
@@ -1466,6 +1478,14 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
 
   if (GuardedControlStack)
     CmdArgs.push_back("-mguarded-control-stack");
+
+  if (HardenPACRetArg) {
+    if (Scope == "none")
+      D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
+    else
+      CmdArgs.push_back(Args.MakeArgString(Twine("-mharden-pac-ret=") +
+                                           HardenPACRetArg->getValue()));
+  }
 }
 
 void Clang::AddARMTargetArgs(const llvm::Triple &Triple, const ArgList &Args,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index d762bcd789bf5..a514be7fd217f 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3428,8 +3428,11 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
 
   TargetInfo::BranchProtectionInfo BPI{};
   StringRef DiagMsg;
-  if (ParsedAttrs.BranchProtection.empty())
+  if (ParsedAttrs.BranchProtection.empty()) {
+    if (!ParsedAttrs.SignReturnAddrHardening.empty())
+      Diag(LiteralLoc, diag::warn_attribute_harden_pac_ret_requires_pac_ret);
     return false;
+  }
   if (!Context.getTargetInfo().validateBranchProtection(
           ParsedAttrs.BranchProtection, ParsedAttrs.CPU, BPI,
           Context.getLangOpts(), DiagMsg)) {
@@ -3442,6 +3445,19 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
   if (!DiagMsg.empty())
     Diag(LiteralLoc, diag::warn_unsupported_branch_protection_spec) << DiagMsg;
 
+  if (!ParsedAttrs.SignReturnAddrHardening.empty()) {
+    auto SignReturnAddrOption =
+        Context.getTargetInfo().validateSignReturnAddressHardening(
+            ParsedAttrs.SignReturnAddrHardening);
+    if (!SignReturnAddrOption)
+      return Diag(LiteralLoc, diag::err_invalid_harden_pac_ret_spec)
+             << ParsedAttrs.SignReturnAddrHardening;
+
+    if (BPI.SignReturnAddr == LangOptions::SignReturnAddressScopeKind::None)
+      return Diag(LiteralLoc,
+                  diag::warn_attribute_harden_pac_ret_requires_pac_ret);
+  }
+
   return false;
 }
 
diff --git a/clang/test/CodeGen/aarch64-sign-return-address-harden.c b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
new file mode 100644
index 0000000000000..6d4db64c5efde
--- /dev/null
+++ b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
@@ -0,0 +1,13 @@
+// RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=none \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret -mharden-pac-ret=none \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address \
+// RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
+
+void foo() {}
+
+// CHECK-NO-HARDEN-NOT: attributes #0 = {{.*}}"sign-return-address-harden"
+// CHECK-HARDEN:        attributes #0 = {{.*}}"sign-return-address-harden"="load-return-address"
\ No newline at end of file
diff --git a/clang/test/Driver/aarch64-security-options.c b/clang/test/Driver/aarch64-security-options.c
index 146add2d1cf70..fbcf3d75071c6 100644
--- a/clang/test/Driver/aarch64-security-options.c
+++ b/clang/test/Driver/aarch64-security-options.c
@@ -32,6 +32,36 @@
 
 // WARN-NOT: warning: ignoring '-mbranch-protection=' option because the 'aarch64' architecture does not support it [-Wbranch-protection]
 
+// RUN: %clang -target aarch64 -c %s -### -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+
+// RUN: %clang -target aarch64 -c %s -### -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=none -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=none -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=pac-ret -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-NONE
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-NONE
+
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=load-return-address 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA
+
+// NO-RA-HARDEN:        ignoring '-mharden-pac-ret' as it requires return address signing
+// NO-RA-HARDEN-NOT:    "-mharden-pac-ret"
+// NO-RA-HARDEN-LRA:    ignoring '-mharden-pac-ret' as it requires return address signing
+// RA-HARDEN-NONE:      "-mharden-pac-ret=none"
+// RA-HARDEN-LRA:       "-mharden-pac-ret=load-return-address"
+
 // RA-OFF: "-msign-return-address=none"
 // RA-NON-LEAF: "-msign-return-address=non-leaf"
 // RA-ALL: "-msign-return-address=all"
diff --git a/clang/test/Driver/arm-security-options.c b/clang/test/Driver/arm-security-options.c
index 613945c24eede..db1888bec52db 100644
--- a/clang/test/Driver/arm-security-options.c
+++ b/clang/test/Driver/arm-security-options.c
@@ -75,6 +75,23 @@
 // RUN: %clang -target arm-arm-none-eabi -march=armv7-r -c %s -### -mbranch-protection=bti 2>&1 | \
 // RUN: FileCheck %s --check-prefix=INCOMPATIBLE-ARCH
 
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8m.main -c %s -### -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mbranch-protection=bti -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mbranch-protection=pac-ret -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mbranch-protection=pac-ret+bti -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -mbranch-protection=pac-ret+leaf -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -msign-return-address=all -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+// RUN: not %clang -target arm-arm-none-eabi -march=armv8.1-m.main -c %s -### -msign-return-address=non-leaf -mharden-pac-ret=none 2>&1 | \
+// RUN: FileCheck %s --check-prefix=RA-HARDEN-INCOMPATIBLE-ARCH
+
 // RA-OFF: "-msign-return-address=none"
 // RA-NON-LEAF: "-msign-return-address=non-leaf"
 // RA-ALL: "-msign-return-address=all"
@@ -91,3 +108,5 @@
 // BAD-LEAF-COMBINATION: unsupported argument 'leaf' to option '-mbranch-protection='
 
 // INCOMPATIBLE-ARCH: '-mbranch-protection=' option is incompatible with the '{{.*}}' architecture
+
+// RA-HARDEN-INCOMPATIBLE-ARCH: unsupported option '-mharden-pac-ret=' for target 'arm-arm-none-eabi'
diff --git a/clang/test/Frontend/aarch64-harden-pac-ret-err.c b/clang/test/Frontend/aarch64-harden-pac-ret-err.c
new file mode 100644
index 0000000000000..9fc2cba7e8205
--- /dev/null
+++ b/clang/test/Frontend/aarch64-harden-pac-ret-err.c
@@ -0,0 +1,3 @@
+// RUN: not %clang_cc1 -fsyntax-only -triple aarch64 %s -mharden-pac-ret=foo 2>&1 | FileCheck %s
+
+// CHECK: invalid value 'foo' in '-mharden-pac-ret=foo'
diff --git a/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c b/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
new file mode 100644
index 0000000000000..3e218253137d6
--- /dev/null
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple aarch64 -verify -fsyntax-only %s
+
+__attribute__((target("harden-pac-ret=none"))) // expected-warning {{'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored}}
+void
+badvalue0(void) {}
+
+__attribute__((target("harden-pac-ret=load-return-address"))) // expected-warning {{'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored}}
+void
+badvalue1(void) {}
+
+__attribute__((target("branch-protection=bti,harden-pac-ret=none"))) // expected-warning {{'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored}}
+void
+badvalue2(void) {}
+
+__attribute__((target("branch-protection=bti,harden-pac-ret=load-return-address"))) // expected-warning {{'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored}}
+void
+badvalue3(void) {}
+
+__attribute__((target("branch-protection=bti,harden-pac-ret=inexistent"))) // expected-error {{invalid or misplaced pac-ret hardening specification 'inexistent'}}
+void
+badvalue4(void) {}
diff --git a/clang/test/Sema/aarch64-harden-pac-ret-attr.c b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
new file mode 100644
index 0000000000000..c05e33ee28055
--- /dev/null
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -triple aarch64 -emit-llvm  -target-cpu generic -target-feature +v8.5a %s -o - \
+// RUN: | FileCheck %s --check-prefix=CHECK
+
+__attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=none")))
+void f1() {}
+// CHECK: define{{.*}} void @f1() #[[#F1:]]
+
+__attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=load-return-address")))
+void f2() {}
+// CHECK: define{{.*}} void @f2() #[[#F2:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf,harden-pac-ret=none")))
+void f3() {}
+// CHECK: define{{.*}} void @f3() #[[#F3:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf,harden-pac-ret=load-return-address")))
+void f4() {}
+// CHECK: define{{.*}} void @f4() #[[#F4:]]
+
+__attribute__ ((target("branch-protection=pac-ret+b-key,harden-pac-ret=none")))
+void f5() {}
+// CHECK: define{{.*}} void @f5() #[[#F5:]]
+
+__attribute__ ((target("branch-protection=pac-ret+b-key,harden-pac-ret=load-return-address")))
+void f6() {}
+// CHECK: define{{.*}} void @f6() #[[#F6:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=none")))
+void f7() {}
+// CHECK: define{{.*}} void @f7() #[[#F7:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=load-return-address")))
+void f8() {}
+// CHECK: define{{.*}} void @f8() #[[#F8:]]
+
+// CHECK-DAG: attributes #[[#F1]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="none"
+// CHECK-DAG: attributes #[[#F2]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address"
+// CHECK-DAG: attributes #[[#F3]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="none"
+// CHECK-DAG: attributes #[[#F4]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address"
+// CHECK-DAG: attributes #[[#F5]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="none" "sign-return-address-key"="b_key"
+// CHECK-DAG: attributes #[[#F6]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
+// CHECK-DAG: attributes #[[#F7]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="none" "sign-return-address-key"="b_key"
+// CHECK-DAG: attributes #[[#F8]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"

>From 97c8d752b6115d3a2e7d6e4c84c35ea0baa641bc Mon Sep 17 00:00:00 2001
From: Victor Campos <victor.campos at arm.com>
Date: Mon, 13 Apr 2026 11:39:18 +0100
Subject: [PATCH 2/2] Address code reviews

---
 .../clang/Basic/DiagnosticSemaKinds.td        |   2 +-
 clang/include/clang/Basic/LangOptions.h       |   5 -
 clang/include/clang/Basic/TargetInfo.h        |   4 +-
 clang/lib/Basic/Targets/AArch64.cpp           |   3 +-
 clang/lib/Basic/Targets/AArch64.h             |   2 +-
 clang/lib/CodeGen/TargetInfo.cpp              |  13 ++
 clang/lib/CodeGen/Targets/AArch64.cpp         |  13 +-
 clang/lib/Driver/ToolChains/Clang.cpp         | 127 +++++++++---------
 clang/lib/Sema/SemaDeclAttr.cpp               |  12 +-
 .../aarch64-sign-return-address-harden.c      |   8 ++
 clang/test/Driver/aarch64-security-options.c  |  17 ++-
 .../aarch64-harden-pac-ret-attr-err-warn.c    |   2 +-
 clang/test/Sema/aarch64-harden-pac-ret-attr.c |  31 +++--
 13 files changed, 144 insertions(+), 95 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fdfe7792441b9..e963eb7d1399b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3388,7 +3388,7 @@ def warn_attribute_harden_pac_ret_requires_pac_ret: Warning<
   "'harden-pac-ret' attribute requires 'branch-protection=pac-ret'; 'target' attribute ignored">,
   InGroup<IgnoredAttributes>;
 def err_invalid_harden_pac_ret_spec : Error<
-  "invalid or misplaced pac-ret hardening specification '%0'">;
+  "invalid or misspelled pac-ret hardening specification '%0'">;
 def err_attribute_invalid_atomic_argument : Error<
   "invalid argument '%0' to atomic attribute; valid options are: "
   "'remote_memory', 'fine_grained_memory', 'ignore_denormal_mode' (optionally "
diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index c0e82b3edd2cb..bc7deae9c5122 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -735,11 +735,6 @@ class LangOptions : public LangOptionsBase {
     return getSignReturnAddressScope() == SignReturnAddressScopeKind::All;
   }
 
-  bool hasSignReturnAddressHardening() const {
-    return getSignReturnAddressHardening() !=
-           SignReturnAddressHardeningKind::None;
-  }
-
   bool isSYCL() const { return SYCLIsDevice || SYCLIsHost; }
 
   bool hasDefaultVisibilityExportMapping() const {
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index 10779518275b4..7d64a9d7bd4bb 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1551,9 +1551,9 @@ class TargetInfo : public TransferrableTargetInfo,
     return false;
   }
 
-  /// Validate the Return Address Signing Hardening specification
+  /// Parse the Return Address Signing Hardening specification.
   virtual std::optional<LangOptions::SignReturnAddressHardeningKind>
-  validateSignReturnAddressHardening(StringRef Spec) const {
+  parseSignReturnAddressHardening(StringRef Spec) const {
     return std::nullopt;
   }
 
diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp
index 639540fc13d87..da10cd384b963 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -277,8 +277,7 @@ bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
 }
 
 std::optional<LangOptions::SignReturnAddressHardeningKind>
-AArch64TargetInfo::validateSignReturnAddressHardening(StringRef Spec) const {
-  assert(!Spec.empty() && "Spec must not be empty");
+AArch64TargetInfo::parseSignReturnAddressHardening(StringRef Spec) const {
   return llvm::StringSwitch<
              std::optional<LangOptions::SignReturnAddressHardeningKind>>(Spec)
       .Case("load-return-address",
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index c0e52319ba628..9312b785dd20b 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -148,7 +148,7 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
                                 const LangOptions &LO,
                                 StringRef &Err) const override;
   std::optional<LangOptions::SignReturnAddressHardeningKind>
-  validateSignReturnAddressHardening(StringRef Spec) const override;
+  parseSignReturnAddressHardening(StringRef Spec) const override;
 
   bool isValidCPUName(StringRef Name) const override;
   void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;
diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp
index 530304f9530ad..9e0b7bb709cc9 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -230,6 +230,7 @@ void TargetCodeGenInfo::setBranchProtectionFnAttributes(
   // Called on already created and initialized function where attributes already
   // set from command line attributes but some might need to be removed as the
   // actual BPI is different.
+
   if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) {
     F.addFnAttr("sign-return-address", BPI.getSignReturnAddrStr());
     F.addFnAttr("sign-return-address-key", BPI.getSignKeyStr());
@@ -244,6 +245,14 @@ void TargetCodeGenInfo::setBranchProtectionFnAttributes(
       F.removeFnAttr("sign-return-address-key");
   }
 
+  if (BPI.SignReturnAddressHardening ==
+      LangOptions::SignReturnAddressHardeningKind::None) {
+    F.removeFnAttr("sign-return-address-harden");
+  } else {
+    F.addFnAttr("sign-return-address-harden",
+                BPI.getSignReturnAddressHardeningStr());
+  }
+
   auto AddRemoveAttributeAsSet = [&](bool Set, const StringRef &ModAttr) {
     if (Set)
       F.addFnAttr(ModAttr);
@@ -266,6 +275,10 @@ void TargetCodeGenInfo::initBranchProtectionFnAttributes(
     FuncAttrs.addAttribute("sign-return-address", BPI.getSignReturnAddrStr());
     FuncAttrs.addAttribute("sign-return-address-key", BPI.getSignKeyStr());
   }
+  if (BPI.SignReturnAddressHardening !=
+      LangOptions::SignReturnAddressHardeningKind::None)
+    FuncAttrs.addAttribute("sign-return-address-harden",
+                           BPI.getSignReturnAddressHardeningStr());
   if (BPI.BranchTargetEnforcement)
     FuncAttrs.addAttribute("branch-target-enforcement");
   if (BPI.BranchProtectionPAuthLR)
diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp
index 2d197e27dc115..87219df5cbc83 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -159,10 +159,17 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {
         (void)CGM.getTarget().validateBranchProtection(
             Attr.BranchProtection, Attr.CPU, BPI, CGM.getLangOpts(), Error);
         assert(Error.empty());
+
+        // Hardening is only accepted in the target attribute if PAC-RET is also
+        // present there. Invalid combinations are handled in Sema.
+        if (BPI.SignReturnAddr !=
+            LangOptions::SignReturnAddressScopeKind::None) {
+          if (auto Hardening = CGM.getTarget().parseSignReturnAddressHardening(
+                  Attr.SignReturnAddrHardening)) {
+            BPI.SignReturnAddressHardening = *Hardening;
+          }
+        }
       }
-      if (!Attr.SignReturnAddrHardening.empty())
-        Fn->addFnAttr("sign-return-address-harden",
-                      Attr.SignReturnAddrHardening);
     }
     setBranchProtectionFnAttributes(BPI, *Fn);
     setPointerAuthFnAttributes(CGM.getCodeGenOpts().PointerAuth, *Fn);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index f91e1fd367043..ed200b6ec3ec9 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1381,79 +1381,85 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
   const Arg *HardenPACRetArg = Args.getLastArg(options::OPT_mharden_pac_ret_EQ);
   const Driver &D = TC.getDriver();
 
+  // Check CmdArgs because some toolchains bypass the driver args and add to
+  // the frontend args directly.
+  bool HasPtrauthReturns = llvm::is_contained(CmdArgs, "-fptrauth-returns") ||
+                           Args.hasArgNoClaim(options::OPT_fno_ptrauth_returns,
+                                              options::OPT_fptrauth_returns);
+
   if (HardenPACRetArg) {
     if (!isAArch64) {
       D.Diag(diag::err_drv_unsupported_opt_for_target)
           << HardenPACRetArg->getSpelling() << TC.getTriple().str();
       return;
     }
-    if (!A)
-      D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
+    StringRef ArgValue = HardenPACRetArg->getValue();
+    if (ArgValue != "none" && ArgValue != "load-return-address") {
+      D.Diag(diag::err_drv_unsupported_option_argument)
+          << HardenPACRetArg->getSpelling() << ArgValue;
+      return;
+    }
   }
 
-  if (!A) {
-    if (Triple.isOSOpenBSD() && isAArch64) {
-      CmdArgs.push_back("-msign-return-address=non-leaf");
-      CmdArgs.push_back("-msign-return-address-key=a_key");
-      CmdArgs.push_back("-mbranch-target-enforce");
-    }
+  if (!A && Triple.isOSOpenBSD() && isAArch64) {
+    CmdArgs.push_back("-msign-return-address=non-leaf");
+    CmdArgs.push_back("-msign-return-address-key=a_key");
+    CmdArgs.push_back("-mbranch-target-enforce");
     return;
   }
 
-  if (!(isAArch64 || (Triple.isArmT32() && Triple.isArmMClass())))
+  if (A && !(isAArch64 || (Triple.isArmT32() && Triple.isArmMClass())))
     D.Diag(diag::warn_incompatible_branch_protection_option)
         << Triple.getArchName();
 
-  StringRef Scope, Key;
-  bool IndirectBranches, BranchProtectionPAuthLR, GuardedControlStack;
-
-  if (A->getOption().matches(options::OPT_msign_return_address_EQ)) {
-    Scope = A->getValue();
-    if (Scope != "none" && Scope != "non-leaf" && Scope != "all")
-      D.Diag(diag::err_drv_unsupported_option_argument)
-          << A->getSpelling() << Scope;
-    Key = "a_key";
-    IndirectBranches = Triple.isOSOpenBSD() && isAArch64;
-    BranchProtectionPAuthLR = false;
-    GuardedControlStack = false;
-  } else {
-    StringRef DiagMsg;
-    llvm::ARM::ParsedBranchProtection PBP;
-    bool EnablePAuthLR = false;
-
-    // To know if we need to enable PAuth-LR As part of the standard branch
-    // protection option, it needs to be determined if the feature has been
-    // activated in the `march` argument. This information is stored within the
-    // CmdArgs variable and can be found using a search.
-    if (isAArch64) {
-      auto isPAuthLR = [](const char *member) {
-        llvm::AArch64::ExtensionInfo pauthlr_extension =
-            llvm::AArch64::getExtensionByID(llvm::AArch64::AEK_PAUTHLR);
-        return pauthlr_extension.PosTargetFeature == member;
-      };
+  StringRef Scope = "none", Key;
+  bool IndirectBranches = false, BranchProtectionPAuthLR = false,
+       GuardedControlStack = false;
 
-      if (llvm::any_of(CmdArgs, isPAuthLR))
-        EnablePAuthLR = true;
+  if (A) {
+    if (A->getOption().matches(options::OPT_msign_return_address_EQ)) {
+      Scope = A->getValue();
+      if (Scope != "none" && Scope != "non-leaf" && Scope != "all")
+        D.Diag(diag::err_drv_unsupported_option_argument)
+            << A->getSpelling() << Scope;
+      Key = "a_key";
+      IndirectBranches = Triple.isOSOpenBSD() && isAArch64;
+      BranchProtectionPAuthLR = false;
+      GuardedControlStack = false;
+    } else {
+      StringRef DiagMsg;
+      llvm::ARM::ParsedBranchProtection PBP;
+      bool EnablePAuthLR = false;
+
+      // To know if we need to enable PAuth-LR As part of the standard branch
+      // protection option, it needs to be determined if the feature has been
+      // activated in the `march` argument. This information is stored within
+      // the CmdArgs variable and can be found using a search.
+      if (isAArch64) {
+        auto isPAuthLR = [](const char *member) {
+          llvm::AArch64::ExtensionInfo pauthlr_extension =
+              llvm::AArch64::getExtensionByID(llvm::AArch64::AEK_PAUTHLR);
+          return pauthlr_extension.PosTargetFeature == member;
+        };
+
+        if (llvm::any_of(CmdArgs, isPAuthLR))
+          EnablePAuthLR = true;
+      }
+      if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg,
+                                            EnablePAuthLR))
+        D.Diag(diag::err_drv_unsupported_option_argument)
+            << A->getSpelling() << DiagMsg;
+      if (!isAArch64 && PBP.Key == "b_key")
+        D.Diag(diag::warn_unsupported_branch_protection)
+            << "b-key" << A->getAsString(Args);
+      Scope = PBP.Scope;
+      Key = PBP.Key;
+      BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
+      IndirectBranches = PBP.BranchTargetEnforcement;
+      GuardedControlStack = PBP.GuardedControlStack;
     }
-    if (!llvm::ARM::parseBranchProtection(A->getValue(), PBP, DiagMsg,
-                                          EnablePAuthLR))
-      D.Diag(diag::err_drv_unsupported_option_argument)
-          << A->getSpelling() << DiagMsg;
-    if (!isAArch64 && PBP.Key == "b_key")
-      D.Diag(diag::warn_unsupported_branch_protection)
-          << "b-key" << A->getAsString(Args);
-    Scope = PBP.Scope;
-    Key = PBP.Key;
-    BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
-    IndirectBranches = PBP.BranchTargetEnforcement;
-    GuardedControlStack = PBP.GuardedControlStack;
-  }
-
-  Arg *PtrauthReturnsArg = Args.getLastArg(options::OPT_fptrauth_returns,
-                                           options::OPT_fno_ptrauth_returns);
-  bool HasPtrauthReturns =
-      PtrauthReturnsArg &&
-      PtrauthReturnsArg->getOption().matches(options::OPT_fptrauth_returns);
+  }
+
   // GCS is currently untested with ptrauth-returns, but enabling this could be
   // allowed in future after testing with a suitable system.
   if (Scope != "none" || BranchProtectionPAuthLR || GuardedControlStack) {
@@ -1465,8 +1471,9 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
           << A->getAsString(Args) << "-fptrauth-returns";
   }
 
-  CmdArgs.push_back(
-      Args.MakeArgString(Twine("-msign-return-address=") + Scope));
+  if (A)
+    CmdArgs.push_back(
+        Args.MakeArgString(Twine("-msign-return-address=") + Scope));
   if (Scope != "none")
     CmdArgs.push_back(
         Args.MakeArgString(Twine("-msign-return-address-key=") + Key));
@@ -1480,7 +1487,7 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, const ArgList &Args,
     CmdArgs.push_back("-mguarded-control-stack");
 
   if (HardenPACRetArg) {
-    if (Scope == "none")
+    if (Scope == "none" && !HasPtrauthReturns)
       D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
     else
       CmdArgs.push_back(Args.MakeArgString(Twine("-mharden-pac-ret=") +
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a514be7fd217f..9118f7bef778e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3426,13 +3426,15 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
              << Unsupported << None << CurFeature << Target;
   }
 
-  TargetInfo::BranchProtectionInfo BPI{};
-  StringRef DiagMsg;
   if (ParsedAttrs.BranchProtection.empty()) {
     if (!ParsedAttrs.SignReturnAddrHardening.empty())
       Diag(LiteralLoc, diag::warn_attribute_harden_pac_ret_requires_pac_ret);
     return false;
   }
+
+  TargetInfo::BranchProtectionInfo BPI{};
+  StringRef DiagMsg;
+
   if (!Context.getTargetInfo().validateBranchProtection(
           ParsedAttrs.BranchProtection, ParsedAttrs.CPU, BPI,
           Context.getLangOpts(), DiagMsg)) {
@@ -3446,10 +3448,10 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) {
     Diag(LiteralLoc, diag::warn_unsupported_branch_protection_spec) << DiagMsg;
 
   if (!ParsedAttrs.SignReturnAddrHardening.empty()) {
-    auto SignReturnAddrOption =
-        Context.getTargetInfo().validateSignReturnAddressHardening(
+    auto SignReturnAddrHardenOpt =
+        Context.getTargetInfo().parseSignReturnAddressHardening(
             ParsedAttrs.SignReturnAddrHardening);
-    if (!SignReturnAddrOption)
+    if (!SignReturnAddrHardenOpt)
       return Diag(LiteralLoc, diag::err_invalid_harden_pac_ret_spec)
              << ParsedAttrs.SignReturnAddrHardening;
 
diff --git a/clang/test/CodeGen/aarch64-sign-return-address-harden.c b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
index 6d4db64c5efde..4b078bbc367b3 100644
--- a/clang/test/CodeGen/aarch64-sign-return-address-harden.c
+++ b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
@@ -1,11 +1,19 @@
 // RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=none \
 // RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s -fno-ptrauth-returns \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
 // RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret \
 // RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s -fptrauth-returns \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
 // RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret -mharden-pac-ret=none \
 // RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
+// RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s -fptrauth-returns -mharden-pac-ret=none \
+// RUN: | FileCheck %s --check-prefixes=CHECK-NO-HARDEN
 // RUN: %clang -target aarch64 -S -emit-llvm -o - %s -mbranch-protection=pac-ret -mharden-pac-ret=load-return-address \
 // RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
+// RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s -fptrauth-returns -mharden-pac-ret=load-return-address \
+// RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
 
 void foo() {}
 
diff --git a/clang/test/Driver/aarch64-security-options.c b/clang/test/Driver/aarch64-security-options.c
index fbcf3d75071c6..321205abd8e1a 100644
--- a/clang/test/Driver/aarch64-security-options.c
+++ b/clang/test/Driver/aarch64-security-options.c
@@ -32,6 +32,9 @@
 
 // WARN-NOT: warning: ignoring '-mbranch-protection=' option because the 'aarch64' architecture does not support it [-Wbranch-protection]
 
+// RUN: %clang -target aarch64 -c %s -### 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=ABSENT-RA-HARDEN
+
 // RUN: %clang -target aarch64 -c %s -### -mharden-pac-ret=none 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=NO-RA-HARDEN
 
@@ -56,11 +59,15 @@
 // RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=load-return-address 2>&1 | \
 // RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA
 
-// NO-RA-HARDEN:        ignoring '-mharden-pac-ret' as it requires return address signing
-// NO-RA-HARDEN-NOT:    "-mharden-pac-ret"
-// NO-RA-HARDEN-LRA:    ignoring '-mharden-pac-ret' as it requires return address signing
-// RA-HARDEN-NONE:      "-mharden-pac-ret=none"
-// RA-HARDEN-LRA:       "-mharden-pac-ret=load-return-address"
+// RUN: not %clang -target aarch64 -c %s -### -mbranch-protection=standard -mharden-pac-ret=foo 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=BAD-HARDEN-PROTECTION
+
+// ABSENT-RA-HARDEN-NOT: "-mharden-pac-ret"
+// NO-RA-HARDEN:         ignoring '-mharden-pac-ret' as it requires return address signing
+// NO-RA-HARDEN-NOT:     "-mharden-pac-ret"
+// RA-HARDEN-NONE:       "-mharden-pac-ret=none"
+// RA-HARDEN-LRA:        "-mharden-pac-ret=load-return-address"
+// BAD-HARDEN-PROTECTION: unsupported argument 'foo' to option '-mharden-pac-ret='
 
 // RA-OFF: "-msign-return-address=none"
 // RA-NON-LEAF: "-msign-return-address=non-leaf"
diff --git a/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c b/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
index 3e218253137d6..a98a80c85c819 100644
--- a/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
@@ -16,6 +16,6 @@ __attribute__((target("branch-protection=bti,harden-pac-ret=load-return-address"
 void
 badvalue3(void) {}
 
-__attribute__((target("branch-protection=bti,harden-pac-ret=inexistent"))) // expected-error {{invalid or misplaced pac-ret hardening specification 'inexistent'}}
+__attribute__((target("branch-protection=bti,harden-pac-ret=inexistent"))) // expected-error {{invalid or misspelled pac-ret hardening specification 'inexistent'}}
 void
 badvalue4(void) {}
diff --git a/clang/test/Sema/aarch64-harden-pac-ret-attr.c b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
index c05e33ee28055..28795003e3a5b 100644
--- a/clang/test/Sema/aarch64-harden-pac-ret-attr.c
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
@@ -1,5 +1,8 @@
-// RUN: %clang_cc1 -triple aarch64 -emit-llvm  -target-cpu generic -target-feature +v8.5a %s -o - \
-// RUN: | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -triple aarch64 -emit-llvm -target-cpu generic -target-feature +v8.5a %s -o - | FileCheck %s
+
+// The following test that the function attributes take precedence over command-line options
+// RUN: %clang_cc1 -triple aarch64 -emit-llvm -target-cpu generic -target-feature +v8.5a %s -msign-return-address=all -mharden-pac-ret=none -o - | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64 -emit-llvm -target-cpu generic -target-feature +v8.5a %s -msign-return-address=all -mharden-pac-ret=load-return-address -o - | FileCheck %s
 
 __attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=none")))
 void f1() {}
@@ -33,11 +36,19 @@ __attribute__ ((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=load
 void f8() {}
 // CHECK: define{{.*}} void @f8() #[[#F8:]]
 
-// CHECK-DAG: attributes #[[#F1]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="none"
-// CHECK-DAG: attributes #[[#F2]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address"
-// CHECK-DAG: attributes #[[#F3]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="none"
-// CHECK-DAG: attributes #[[#F4]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address"
-// CHECK-DAG: attributes #[[#F5]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="none" "sign-return-address-key"="b_key"
-// CHECK-DAG: attributes #[[#F6]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
-// CHECK-DAG: attributes #[[#F7]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="none" "sign-return-address-key"="b_key"
-// CHECK-DAG: attributes #[[#F8]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
+// These check patterns rely on the fact that "sign-return-address-harden" appears after "sign-return-address"
+
+// CHECK:     attributes #[[#F1]] = { {{.*}} "sign-return-address"="non-leaf"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     attributes #[[#F2]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address"
+// CHECK:     attributes #[[#F3]] = { {{.*}} "sign-return-address"="all"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     attributes #[[#F4]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address"
+// CHECK:     attributes #[[#F5]] = { {{.*}} "sign-return-address"="non-leaf"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#F6]] = { {{.*}} "sign-return-address"="non-leaf" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#F7]] = { {{.*}} "sign-return-address"="all"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#F8]] = { {{.*}} "sign-return-address"="all" "sign-return-address-harden"="load-return-address" "sign-return-address-key"="b_key"



More information about the cfe-commits mailing list