Author: Victor Campos
Date: 2026-10-08T02:00:01Z
New Revision: dc488b83639cc69ab9ecd06d770badc663720ede

URL: 
https://github.com/llvm/llvm-project/commit/dc488b83639cc69ab9ecd06d770badc663720ede
DIFF: 
https://github.com/llvm/llvm-project/commit/dc488b83639cc69ab9ecd06d770badc663720ede.diff

LOG: [Clang][AArch64] Command-line options for A-profile's Return Address 
Authentication Hardening (#176171)

This patch introduces a new command-line option to enable the AArch64
A-profile's Return Address Authentication 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 from 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 from return address.

The corresponding function attribute takes the option and its possible
values using the same naming. Also, the function attributes take
precedence over the command-line options.

A new Clang documentation page is also added to explain the feature to
end users.

1: https://pacmanattack.com
2:
https://developer.arm.com/documentation/101754/0624/armclang-Reference/armclang-Command-line-Options/-mharden-pac-ret

Added: 
    clang/docs/ReturnAddressAuthenticationHardening.md
    clang/test/CodeGen/aarch64-sign-return-address-harden.c
    clang/test/Frontend/aarch64-harden-pac-ret-err.c
    clang/test/Sema/aarch64-harden-pac-ret-attr-err-warn.c
    clang/test/Sema/aarch64-harden-pac-ret-attr-ignored.c
    clang/test/Sema/aarch64-harden-pac-ret-attr.c

Modified: 
    clang/docs/ReleaseNotes.md
    clang/docs/index.md
    clang/include/clang/Basic/DiagnosticDriverKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Basic/LangOptions.h
    clang/include/clang/Basic/TargetInfo.h
    clang/include/clang/Options/Options.td
    clang/lib/Basic/Targets/AArch64.cpp
    clang/lib/Basic/Targets/AArch64.h
    clang/lib/Basic/Targets/ARM.cpp
    clang/lib/Basic/Targets/ARM.h
    clang/lib/CodeGen/CodeGenModule.cpp
    clang/lib/CodeGen/TargetInfo.cpp
    clang/lib/CodeGen/Targets/AArch64.cpp
    clang/lib/CodeGen/Targets/ARM.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/test/CodeGen/AArch64/sign-return-address.c
    clang/test/Driver/aarch64-security-options.c
    clang/test/Driver/arm-security-options.c
    llvm/docs/ReleaseNotes.md

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3fa6102b6ec72..7134d42865b37 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -298,6 +298,14 @@ features cannot lower the translation-unit ABI level;
   matching on the presumed source location (accounting for macro expansions
   and `#line` directives). (#GH194210)
 
+- Added the AArch64 option `-mharden-pac-ret=load-return-address` to harden
+  return address signing against PACMAN attacks. The option requires return
+  address signing to be enabled and emits a load from the return address before
+  returning, reducing the cache side channel used to guess pointer
+  authentication codes. See
+  {doc}`Return Address Authentication Hardening 
<ReturnAddressAuthenticationHardening>`
+  for more information.
+
 ### Deprecated Compiler Flags
 
 ### Modified Compiler Flags

diff  --git a/clang/docs/ReturnAddressAuthenticationHardening.md 
b/clang/docs/ReturnAddressAuthenticationHardening.md
new file mode 100644
index 0000000000000..fe759a9365df6
--- /dev/null
+++ b/clang/docs/ReturnAddressAuthenticationHardening.md
@@ -0,0 +1,161 @@
+# Return Address Authentication Hardening
+
+## Introduction
+
+Return Address Authentication Hardening is a mitigation against the
+PACMAN attack, which aims to bypass Pointer Authentication on AArch64
+targets. The hardening mechanism described here is specific to pointer
+authentication of return addresses.
+
+Return Address Signing (or Authentication), also known as pac-ret, is a
+feature devised to protect programs against Return Oriented Programming
+(ROP), in which attackers may hijack the return address of functions in
+order to direct execution to malicious code.
+
+pac-ret can be enabled via 
diff erent command-line options:
+
+- `-mbranch-protection=` with `pac-ret`, `pac-ret+leaf` or `standard` as
+  value.
+- `-msign-return-address=` with `non-leaf` or `all` as value.
+- `-fptrauth-returns`.
+
+More information can be found in
+[Pointer Authentication](PointerAuthentication.md).
+
+Return Address Authentication Hardening is a mechanism to strengthen
+Return Address Signing against the PACMAN attack in AArch64 targets. It
+can be enabled with `-mharden-pac-ret=load-return-address`.
+
+## PACMAN attack
+
+PACMAN is an attack that aims to extract valuable information about
+pointer authentication codes using side-channels in speculative
+execution.
+
+It is performed with the use of gadgets to try and guess PAC codes.
+These guesses raise no faults because they are done in speculation. By
+observing the effects of the guessed PAC code on the processor's cache,
+it might be possible to determine the valid PAC code for the address to
+which the attacker wants the program to return.
+
+A usual PACMAN gadget looks like this:
+
+```c
+void function() {
+  ...
+  if (condition)
+    return;
+  ...
+}
+```
+
+Such code would be compiled to:
+
+```asm
+paciasp
+...
+cbz w0, .LBB0_2
+autiasp
+ret
+...
+```
+
+This code fragment may be used as a gadget. A speculative execution of
+this fragment can happen as follows:
+
+- If the Link Register (LR) has the right PAC code, `autiasp` will
+  succeed and strip the PAC code out of it. The processor's instruction
+  fetcher will then bring the code after the return into the cache (that
+  is, the instructions located at the address pointed by LR).
+
+- If the LR has the wrong PAC code, `autiasp` will not succeed and
+  hence will write a predefined error value to the LR's higher bits.
+  Because of this, the instruction fetcher will not bring the code
+  after the return into the cache.
+
+This 
diff erence in behavior is what drives the PACMAN attack. An
+attacker can try to guess PAC codes and monitor cache behavior until the
+code after the return is observed to have been brought into the cache.
+
+Details can be found at [pacmanattack.com](https://pacmanattack.com).
+
+## Hardening
+
+In order to mitigate the PACMAN attack, a hardening mechanism can be
+enabled with `-mharden-pac-ret=load-return-address`.
+
+```asm
+paciasp
+...
+cbz w0, .LBB0_2
+autiasp
+mov     x8, x30
+xpaclri
+ldr     w30, [x30]
+ret     x8
+...
+```
+
+The idea is to always bring the code after the return into cache (the
+instructions located at the address pointed by LR), therefore minimizing
+the 
diff erence between a speculative execution with a correct PAC code
+and with an incorrect one.
+
+- `autiasp` performs the authentication step.
+- `mov x8, x30` copies the return address (LR and x30 are synonyms) to a
+  temporary.
+- `xpaclri` strips the PAC code out of the return address in x30.
+- `ldr w30, [x30]` performs a load from the return address in x30.
+- `ret x8` returns to the authenticated return address.
+
+The load operation brings the code into the cache even if the
+authentication step fails. As a consequence, in either case the code is
+loaded into the cache. Furthermore, the return operation uses the
+original return address before stripping, so the return address
+protection is still kept in place in a normal non-speculative execution.
+
+If FEAT_PAuth is present, the code sequence can make use of instructions
+available with said feature with no change in semantics:
+
+```asm
+autiasp
+mov     x8, x30
+xpaci   x8
+ldr     w8, [x8]
+ret
+```
+
+## Command-line option
+
+Return address authentication hardening can be enabled at translation
+unit level with `-mharden-pac-ret=load-return-address`. It requires
+pac-ret to be enabled at translation unit level as well.
+
+## Function attribute
+
+In addition to the command-line option
+`-mharden-pac-ret=load-return-address`, the developer can enable the
+mitigation at function level with the use of the corresponding function
+attribute.
+
+```c
+__attribute__((target(
+    "branch-protection=pac-ret,harden-pac-ret=load-return-address")))
+void function() {
+  ...
+}
+```
+
+## Caveats
+
+The load from the return address brings the code into the shared
+instruction/data cache, therefore this cache level can't be used as an
+oracle to find out whether the authentication succeeded or not. However,
+in the case of authentication success, the code is also fetched into the
+instruction cache. An attacker who is able to measure this cache level
+specifically may still be able to carry out the exploit.
+
+Another caveat is if the code at the return address contains a load
+operation within the speculation window. If this is the case, this load
+will only execute speculatively if authentication succeeds, thus opening
+the program up for exploitation despite the mitigation.

diff  --git a/clang/docs/index.md b/clang/docs/index.md
index a05210b8e73a0..c67d07d317278 100644
--- a/clang/docs/index.md
+++ b/clang/docs/index.md
@@ -45,6 +45,7 @@ BoundsSafetyAdoptionGuide
 BoundsSafetyImplPlans
 ControlFlowIntegrity
 LTOVisibility
+ReturnAddressAuthenticationHardening
 SafeStack
 ShadowCallStack
 StructureProtection
@@ -142,6 +143,7 @@ LLVMExceptionHandlingCodeGen
 * {doc}`ControlFlowIntegrity`
 * {doc}`LTOVisibility`
 * {doc}`PointerAuthentication`
+* {doc}`ReturnAddressAuthenticationHardening`
 * {doc}`SafeStack`
 * {doc}`ShadowCallStack`
 * {doc}`StructureProtection`
@@ -213,4 +215,3 @@ LLVMExceptionHandlingCodeGen
 
 - {ref}`genindex`
 - {ref}`search`
-

diff  --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td 
b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index e8a66dae748ff..ce0f688108cc7 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -573,6 +573,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 4bb5aec9d04f8..c2035821e723f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3470,6 +3470,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 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.def 
b/clang/include/clang/Basic/LangOptions.def
index e0c29f7b0d791..155d365771b83 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -467,6 +467,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 7539e000d03f9..160c53fed4935 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -320,6 +320,13 @@ class LangOptionsBase {
     BKey
   };
 
+  enum class SignReturnAddressHardeningKind {
+    /// Regular return address signing.
+    None,
+    /// Hardened return address signing with load from the return address.
+    LoadReturnAddress
+  };
+
   enum class ThreadModelKind {
     /// POSIX Threads.
     POSIX,
@@ -764,6 +771,12 @@ class LangOptions : public LangOptionsBase {
     return getSignReturnAddressScope() == SignReturnAddressScopeKind::All;
   }
 
+  /// Check if return address authentication hardening is enabled
+  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 ebf065c0934fa..4d32383879179 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -61,10 +61,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;
@@ -1525,9 +1541,10 @@ class TargetInfo : public TransferrableTargetInfo,
     return false;
   }
 
-  /// Determine if this TargetInfo supports the given branch protection
-  /// specification
-  virtual bool validateBranchProtection(StringRef Spec, StringRef Arch,
+  /// Determines whether this TargetInfo supports the given branch protection
+  /// specification. BPI is modified based on the input arguments. Returns
+  /// whether the specification is valid.
+  virtual bool validateBranchProtection(const ParsedTargetAttr &Attr,
                                         BranchProtectionInfo &BPI,
                                         const LangOptions &LO,
                                         StringRef &Err) const {
@@ -1535,6 +1552,12 @@ class TargetInfo : public TransferrableTargetInfo,
     return false;
   }
 
+  /// Parse the Return Address Authentication Hardening specification.
+  virtual std::optional<LangOptions::SignReturnAddressHardeningKind>
+  parseSignReturnAddressHardening(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 f3e50f9083532..4cea4db32b93b 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -6015,6 +6015,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 authentication hardening scheme">,
+  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 a4841514be35e..c8ce67bf780a7 100644
--- a/clang/lib/Basic/Targets/AArch64.cpp
+++ b/clang/lib/Basic/Targets/AArch64.cpp
@@ -248,13 +248,13 @@ bool AArch64TargetInfo::validateGlobalRegisterVariable(
          getTargetOpts().FeatureMap.lookup(("reserve-x" + RegNum).str());
 }
 
-bool AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
+bool AArch64TargetInfo::validateBranchProtection(const ParsedTargetAttr &Attr,
                                                  BranchProtectionInfo &BPI,
                                                  const LangOptions &LO,
                                                  StringRef &Err) const {
   llvm::ARM::ParsedBranchProtection PBP;
-  if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, getTriple(),
-                                        HasPAuthLR))
+  if (!llvm::ARM::parseBranchProtection(Attr.BranchProtection, PBP, Err,
+                                        getTriple(), HasPAuthLR))
     return false;
 
   // GCS is currently untested with ptrauth-returns, but enabling this could be
@@ -275,12 +275,29 @@ bool 
AArch64TargetInfo::validateBranchProtection(StringRef Spec, StringRef,
   else
     BPI.SignKey = LangOptions::SignReturnAddressKeyKind::BKey;
 
+  if (Attr.SignReturnAddrHardening.empty())
+    BPI.SignReturnAddressHardening =
+        LangOptions::SignReturnAddressHardeningKind::None;
+  else if (auto Hardening =
+               parseSignReturnAddressHardening(Attr.SignReturnAddrHardening))
+    BPI.SignReturnAddressHardening = *Hardening;
+
   BPI.BranchTargetEnforcement = PBP.BranchTargetEnforcement;
   BPI.BranchProtectionPAuthLR = PBP.BranchProtectionPAuthLR;
   BPI.GuardedControlStack = PBP.GuardedControlStack;
   return true;
 }
 
+std::optional<LangOptions::SignReturnAddressHardeningKind>
+AArch64TargetInfo::parseSignReturnAddressHardening(StringRef Spec) const {
+  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();
 }
@@ -1411,6 +1428,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 3ccfa265399be..aeea2ab16ea59 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -140,10 +140,12 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public 
TargetInfo {
   StringRef getABI() const override;
   bool setABI(const std::string &Name) override;
 
-  bool validateBranchProtection(StringRef Spec, StringRef Arch,
+  bool validateBranchProtection(const ParsedTargetAttr &Attr,
                                 BranchProtectionInfo &BPI,
                                 const LangOptions &LO,
                                 StringRef &Err) const override;
+  std::optional<LangOptions::SignReturnAddressHardeningKind>
+  parseSignReturnAddressHardening(StringRef Spec) const override;
 
   bool isValidCPUName(StringRef Name) const override;
   void fillValidCPUList(SmallVectorImpl<StringRef> &Values) const override;

diff  --git a/clang/lib/Basic/Targets/ARM.cpp b/clang/lib/Basic/Targets/ARM.cpp
index 0e424d031700b..221a1dc2a2ed4 100644
--- a/clang/lib/Basic/Targets/ARM.cpp
+++ b/clang/lib/Basic/Targets/ARM.cpp
@@ -369,14 +369,16 @@ bool 
ARMTargetInfo::isBranchProtectionSupportedArch(StringRef Arch) const {
   return a.isArmT32() && (Profile == llvm::ARM::ProfileKind::M);
 }
 
-bool ARMTargetInfo::validateBranchProtection(StringRef Spec, StringRef Arch,
+bool ARMTargetInfo::validateBranchProtection(const ParsedTargetAttr &Attr,
                                              BranchProtectionInfo &BPI,
                                              const LangOptions &LO,
                                              StringRef &Err) const {
   llvm::ARM::ParsedBranchProtection PBP;
-  if (!llvm::ARM::parseBranchProtection(Spec, PBP, Err, getTriple()))
+  if (!llvm::ARM::parseBranchProtection(Attr.BranchProtection, PBP, Err,
+                                        getTriple()))
     return false;
 
+  StringRef Arch = Attr.CPU.empty() ? getTargetOpts().CPU : Attr.CPU;
   if (!isBranchProtectionSupportedArch(Arch))
     return false;
 

diff  --git a/clang/lib/Basic/Targets/ARM.h b/clang/lib/Basic/Targets/ARM.h
index 976e1e06ae9b2..a9ab0373a7f5e 100644
--- a/clang/lib/Basic/Targets/ARM.h
+++ b/clang/lib/Basic/Targets/ARM.h
@@ -146,7 +146,7 @@ class LLVM_LIBRARY_VISIBILITY ARMTargetInfo : public 
TargetInfo {
   bool setABI(const std::string &Name) override;
 
   bool isBranchProtectionSupportedArch(StringRef Arch) const override;
-  bool validateBranchProtection(StringRef Spec, StringRef Arch,
+  bool validateBranchProtection(const ParsedTargetAttr &Attr,
                                 BranchProtectionInfo &BPI,
                                 const LangOptions &LO,
                                 StringRef &Err) const override;

diff  --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index b4ebc6b6abff0..33339c43e8a16 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1711,6 +1711,14 @@ void CodeGenModule::Release() {
               LangOpts.PointerAuthInitFiniAddressDiscrimination);
     }
 
+    if (LangOpts.hasSignReturnAddressHardening()) {
+      TargetInfo::BranchProtectionInfo BPI(LangOpts);
+      getModule().addModuleFlag(
+          llvm::Module::Error, "sign-return-address-harden",
+          llvm::MDString::get(getLLVMContext(),
+                              BPI.getSignReturnAddressHardeningStr()));
+    }
+
     if (getTriple().isOSLinux()) {
       getModule().addModuleFlag(llvm::Module::Error, 
"ptrauth-sign-personality",
                                 LangOpts.PointerAuthCalls);

diff  --git a/clang/lib/CodeGen/TargetInfo.cpp 
b/clang/lib/CodeGen/TargetInfo.cpp
index 2f89154c2d2d9..9316cc335b2c2 100644
--- a/clang/lib/CodeGen/TargetInfo.cpp
+++ b/clang/lib/CodeGen/TargetInfo.cpp
@@ -216,6 +216,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 
diff erent.
+
   if (BPI.SignReturnAddr != LangOptions::SignReturnAddressScopeKind::None) {
     F.addFnAttr("sign-return-address", BPI.getSignReturnAddrStr());
     F.addFnAttr("sign-return-address-key", BPI.getSignKeyStr());
@@ -226,6 +227,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);
@@ -248,6 +257,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 23a4e5c0ce9ec..bdbe1ad718f7a 100644
--- a/clang/lib/CodeGen/Targets/AArch64.cpp
+++ b/clang/lib/CodeGen/Targets/AArch64.cpp
@@ -157,7 +157,7 @@ class AArch64TargetCodeGenInfo : public TargetCodeGenInfo {
       if (!Attr.BranchProtection.empty()) {
         StringRef Error;
         (void)CGM.getTarget().validateBranchProtection(
-            Attr.BranchProtection, Attr.CPU, BPI, CGM.getLangOpts(), Error);
+            Attr, BPI, CGM.getLangOpts(), Error);
         assert(Error.empty());
       }
     }

diff  --git a/clang/lib/CodeGen/Targets/ARM.cpp 
b/clang/lib/CodeGen/Targets/ARM.cpp
index c6435dd6f5a8b..17bae6b7c2021 100644
--- a/clang/lib/CodeGen/Targets/ARM.cpp
+++ b/clang/lib/CodeGen/Targets/ARM.cpp
@@ -149,7 +149,7 @@ class ARMTargetCodeGenInfo : public TargetCodeGenInfo {
         StringRef Arch =
             Attr.CPU.empty() ? CGM.getTarget().getTargetOpts().CPU : Attr.CPU;
         if (!CGM.getTarget().validateBranchProtection(
-                Attr.BranchProtection, Arch, BPI, CGM.getLangOpts(), DiagMsg)) 
{
+                Attr, BPI, CGM.getLangOpts(), DiagMsg)) {
           CGM.getDiags().Report(
               D->getLocation(),
               diag::warn_target_unsupported_branch_protection_attribute)

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 2325e7cc552ef..63e7c42549d3f 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -1390,16 +1390,59 @@ 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;
+    }
+    StringRef ArgValue = HardenPACRetArg->getValue();
+    if (ArgValue != "none" && ArgValue != "load-return-address") {
+      D.Diag(diag::err_drv_unsupported_option_argument)
+          << HardenPACRetArg->getSpelling() << ArgValue;
+      return;
+    }
+    if (ArgValue != "none" &&
+        Args.hasFlagNoClaim(options::OPT_mexecute_only,
+                            options::OPT_mno_execute_only, false)) {
+      D.Diag(diag::err_drv_incompatible_options)
+          << HardenPACRetArg->getAsString(Args) << "-mexecute-only";
+      return;
+    }
+  }
+
+  // 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.hasFlagNoClaim(options::OPT_fptrauth_returns,
+                          options::OPT_fno_ptrauth_returns, false);
+
+  auto RenderHardenPACRet = [&](StringRef Scope) {
+    if (!HardenPACRetArg)
+      return;
+    if (Scope == "none" && !HasPtrauthReturns)
+      D.Diag(diag::warn_harden_pac_ret_requires_pac_ret);
+    else
+      CmdArgs.push_back(Args.MakeArgString(Twine("-mharden-pac-ret=") +
+                                           HardenPACRetArg->getValue()));
+  };
+
   if (!A) {
     if ((Triple.isOSOpenBSD() || Triple.isAndroid()) && isAArch64) {
       CmdArgs.push_back("-msign-return-address=non-leaf");
       CmdArgs.push_back("-msign-return-address-key=a_key");
       CmdArgs.push_back("-mbranch-target-enforce");
+      RenderHardenPACRet("non-leaf");
+    } else {
+      RenderHardenPACRet("none");
     }
     return;
   }
 
-  const Driver &D = TC.getDriver();
   if (!(isAArch64 || (Triple.isArmT32() && Triple.isArmMClass())))
     D.Diag(diag::warn_incompatible_branch_protection_option)
         << Triple.getArchName();
@@ -1453,11 +1496,6 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, 
const ArgList &Args,
     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) {
@@ -1482,6 +1520,8 @@ static void CollectARMPACBTIOptions(const ToolChain &TC, 
const ArgList &Args,
 
   if (GuardedControlStack)
     CmdArgs.push_back("-mguarded-control-stack");
+
+  RenderHardenPACRet(Scope);
 }
 
 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 db9138214351b..05974095b8b25 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3709,13 +3709,18 @@ bool Sema::checkTargetAttr(SourceLocation LiteralLoc, 
StringRef AttrStr) {
              << Unsupported << None << CurFeature << Target;
   }
 
+  if (ParsedAttrs.BranchProtection.empty()) {
+    if (!ParsedAttrs.SignReturnAddrHardening.empty())
+      return Diag(LiteralLoc,
+                  diag::warn_attribute_harden_pac_ret_requires_pac_ret);
+    return false;
+  }
+
   TargetInfo::BranchProtectionInfo BPI{};
   StringRef DiagMsg;
-  if (ParsedAttrs.BranchProtection.empty())
-    return false;
+
   if (!Context.getTargetInfo().validateBranchProtection(
-          ParsedAttrs.BranchProtection, ParsedAttrs.CPU, BPI,
-          Context.getLangOpts(), DiagMsg)) {
+          ParsedAttrs, BPI, Context.getLangOpts(), DiagMsg)) {
     if (DiagMsg.empty())
       return Diag(LiteralLoc, diag::warn_unsupported_target_attribute)
              << Unsupported << None << "branch-protection" << Target;
@@ -3725,6 +3730,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 SignReturnAddrHardenOpt =
+        Context.getTargetInfo().parseSignReturnAddressHardening(
+            ParsedAttrs.SignReturnAddrHardening);
+    if (!SignReturnAddrHardenOpt)
+      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.c 
b/clang/test/CodeGen/AArch64/sign-return-address.c
index c617b45c21317..f5dea76a6a267 100644
--- a/clang/test/CodeGen/AArch64/sign-return-address.c
+++ b/clang/test/CodeGen/AArch64/sign-return-address.c
@@ -6,6 +6,7 @@
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - 
-mbranch-protection=pac-ret+leaf  %s | FileCheck %s --check-prefix=CHECK 
--check-prefix=ALL
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - 
-mbranch-protection=pac-ret+b-key %s | FileCheck %s --check-prefix=CHECK 
--check-prefix=B-KEY
 // RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - 
-mbranch-protection=bti %s           | FileCheck %s --check-prefix=CHECK 
--check-prefix=BTE
+// RUN: %clang -target aarch64-none-elf -S -emit-llvm -o - 
-mbranch-protection=pac-ret -mharden-pac-ret=load-return-address %s | FileCheck 
%s --check-prefix=CHECK --check-prefix=HARDEN
 // RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - 
-mbranch-protection=pac-ret %s | FileCheck %s --check-prefix=CHECK 
--check-prefix=WIN-PAC
 // RUN: %clang -target aarch64-windows-msvc -S -emit-llvm -o - 
-mbranch-protection=standard %s | FileCheck %s --check-prefix=CHECK 
--check-prefix=WIN-STD
 
@@ -19,49 +20,63 @@
 // NONE-NOT:  attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"
 // NONE-NOT:  attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement"
 
-// ALL:   attributes #[[#ATTR]] = { {{.*}} "sign-return-address"
-// PART:  attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="a_key"
-// B-KEY: attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="b_key"
-// BTE:   attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement"
+// ALL:     attributes #[[#ATTR]] = { {{.*}} "sign-return-address"
+// PART:    attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="a_key"
+// B-KEY:   attributes #[[#ATTR]] = { {{.*}} "sign-return-address-key"="b_key"
+// BTE:     attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement"
+// HARDEN:  attributes #[[#ATTR]] = { {{.*}} 
"sign-return-address-harden"="load-return-address"
 // WIN-PAC: attributes #[[#ATTR]] = { {{.*}} "sign-return-address"="non-leaf" 
"sign-return-address-key"="b_key"
 // WIN-STD: attributes #[[#ATTR]] = { {{.*}} "branch-target-enforcement" 
{{.*}} "guarded-control-stack" {{.*}} "sign-return-address"="non-leaf" 
"sign-return-address-key"="b_key"
 
 
 // Check module attributes
 
-// NONE-NOT:  !"branch-target-enforcement"
-// ALL-NOT:   !"branch-target-enforcement"
-// PART-NOT:  !"branch-target-enforcement"
-// BTE:       !{i32 8, !"branch-target-enforcement", i32 2}
-// B-KEY-NOT: !"branch-target-enforcement"
+// NONE-NOT:    !"branch-target-enforcement"
+// ALL-NOT:     !"branch-target-enforcement"
+// PART-NOT:    !"branch-target-enforcement"
+// BTE:         !{i32 8, !"branch-target-enforcement", i32 2}
+// B-KEY-NOT:   !"branch-target-enforcement"
+// HARDEN-NOT:  !"branch-target-enforcement"
 // WIN-PAC-NOT: !"branch-target-enforcement"
-// WIN-STD:   !{i32 8, !"branch-target-enforcement", i32 2}
+// WIN-STD:     !{i32 8, !"branch-target-enforcement", i32 2}
 
 // WIN-PAC-NOT: !"guarded-control-stack"
-// WIN-STD: !{i32 8, !"guarded-control-stack", i32 2}
+// WIN-STD:     !{i32 8, !"guarded-control-stack", i32 2}
 
-// NONE-NOT:  !"sign-return-address"
-// ALL:   !{i32 8, !"sign-return-address", i32 2}
-// PART:  !{i32 8, !"sign-return-address", i32 2}
-// BTE-NOT:   !"sign-return-address"
-// B-KEY: !{i32 8, !"sign-return-address", i32 2}
-// WIN-PAC: !{i32 8, !"sign-return-address", i32 2}
-// WIN-STD: !{i32 8, !"sign-return-address", i32 2}
+// NONE-NOT: !"sign-return-address"
+// ALL:      !{i32 8, !"sign-return-address", i32 2}
+// PART:     !{i32 8, !"sign-return-address", i32 2}
+// BTE-NOT:  !"sign-return-address"
+// B-KEY:    !{i32 8, !"sign-return-address", i32 2}
+// HARDEN:   !{i32 8, !"sign-return-address", i32 2}
+// WIN-PAC:  !{i32 8, !"sign-return-address", i32 2}
+// WIN-STD:  !{i32 8, !"sign-return-address", i32 2}
 
-// NONE-NOT:  !"sign-return-address-all"
-// ALL:   !{i32 8, !"sign-return-address-all", i32 2}
-// PART-NOT:  !"sign-return-address-all"
-// BTE-NOT:   !"sign-return-address-all"
-// B-KEY-NOT: !"sign-return-address-all"
+// NONE-NOT:    !"sign-return-address-all"
+// ALL:         !{i32 8, !"sign-return-address-all", i32 2}
+// PART-NOT:    !"sign-return-address-all"
+// BTE-NOT:     !"sign-return-address-all"
+// B-KEY-NOT:   !"sign-return-address-all"
+// HARDEN-NOT:  !"sign-return-address-all"
 // WIN-PAC-NOT: !"sign-return-address-all"
 // WIN-STD-NOT: !"sign-return-address-all"
 
-// NONE-NOT:  !"sign-return-address-with-bkey"
-// ALL-NOT:   !"sign-return-address-with-bkey"
-// PART-NOT:  !"sign-return-address-with-bkey"
-// BTE-NOT:   !"sign-return-address-with-bkey"
-// B-KEY: !{i32 8, !"sign-return-address-with-bkey", i32 2}
-// WIN-PAC: !{i32 8, !"sign-return-address-with-bkey", i32 2}
-// WIN-STD: !{i32 8, !"sign-return-address-with-bkey", i32 2}
+// NONE-NOT:   !"sign-return-address-with-bkey"
+// ALL-NOT:    !"sign-return-address-with-bkey"
+// PART-NOT:   !"sign-return-address-with-bkey"
+// BTE-NOT:    !"sign-return-address-with-bkey"
+// HARDEN-NOT: !"sign-return-address-with-bkey"
+// B-KEY:      !{i32 8, !"sign-return-address-with-bkey", i32 2}
+// WIN-PAC:    !{i32 8, !"sign-return-address-with-bkey", i32 2}
+// WIN-STD:    !{i32 8, !"sign-return-address-with-bkey", i32 2}
+
+// NONE-NOT:    !"sign-return-address-harden"
+// ALL-NOT:     !"sign-return-address-harden"
+// PART-NOT:    !"sign-return-address-harden"
+// BTE-NOT:     !"sign-return-address-harden"
+// HARDEN:      !{i32 1, !"sign-return-address-harden", !"load-return-address"}
+// B-KEY-NOT:   !"sign-return-address-harden"
+// WIN-PAC-NOT: !"sign-return-address-harden"
+// WIN-STD-NOT: !"sign-return-address-harden"
 
 void foo() {}

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..4c68867fb829b
--- /dev/null
+++ b/clang/test/CodeGen/aarch64-sign-return-address-harden.c
@@ -0,0 +1,27 @@
+// 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
+// RUN: %clang -target aarch64-linux-pauthtest -S -emit-llvm -o - %s 
-mharden-pac-ret=load-return-address \
+// RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
+// RUN: %clang -target aarch64-openbsd -S -emit-llvm -o - %s 
-mharden-pac-ret=load-return-address \
+// RUN: | FileCheck %s --check-prefixes=CHECK-HARDEN
+// RUN: %clang -target aarch64-android -S -emit-llvm -o - %s 
-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 087b4b247bd82..183b49b352ad0 100644
--- a/clang/test/Driver/aarch64-security-options.c
+++ b/clang/test/Driver/aarch64-security-options.c
@@ -24,6 +24,9 @@
 // RUN: %clang --target=aarch64-windows-msvc -c %s -### 
-mbranch-protection=standard                  2>&1 | \
 // RUN: FileCheck %s --check-prefix=RA-NON-LEAF --check-prefix=KEY-B 
--check-prefix=BTE-ON --check-prefix=GCS-ON --check-prefix=WARN
 
+// RUN: not %clang -target arm64-apple-darwin -c %s -### 
-mbranch-protection=standard -fptrauth-returns 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-INCOMPATIBLE-PTRAUTHRETURNS
+
 // If the -msign-return-address and -mbranch-protection are both used, the
 // right-most one controls return address signing.
 // RUN: %clang --target=aarch64 -c %s -### -msign-return-address=non-leaf 
-mbranch-protection=none     2>&1 | \
@@ -55,6 +58,61 @@
 
 // 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
+
+// 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 arm64-apple-darwin -c %s -### -fno-ptrauth-returns 
-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 arm64-apple-darwin -c %s -### -fno-ptrauth-returns 
-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 arm64-apple-darwin -c %s -### -fptrauth-returns 
-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 arm64-apple-darwin -c %s -### -fptrauth-returns 
-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
+
+// RUN: not %clang -target aarch64 -c %s -### -mbranch-protection=standard 
-mharden-pac-ret=foo 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=BAD-HARDEN-PROTECTION
+// RUN: not %clang -target arm64-apple-darwin -c %s -### -fptrauth-returns 
-mharden-pac-ret=foo 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=BAD-HARDEN-PROTECTION
+
+// RUN: not %clang -target aarch64 -c %s -### -mbranch-protection=standard 
-mharden-pac-ret=load-return-address -mexecute-only 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-LRA-INCOMPATIBLE-EXEC-ONLY
+// RUN: %clang -target aarch64 -c %s -### -mbranch-protection=standard 
-mharden-pac-ret=none -mexecute-only 2>&1 | \
+// RUN: FileCheck %s --check-prefixes=RA-HARDEN-NONE-COMPATIBLE-EXEC-ONLY
+
+
+// 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-HARDEN-LRA-INCOMPATIBLE-EXEC-ONLY: the combination of 
'-mharden-pac-ret=load-return-address' and '-mexecute-only' is incompatible
+// RA-HARDEN-NONE-COMPATIBLE-EXEC-ONLY-NOT: the combination of 
'-mharden-pac-ret={{.*}}' and '-mexecute-only' is incompatible
+
 // RA-OFF: "-msign-return-address=none"
 // RA-NON-LEAF: "-msign-return-address=non-leaf"
 // RA-ALL: "-msign-return-address=all"
@@ -63,6 +121,8 @@
 // KEY-B: "-msign-return-address-key=b_key"
 // KEY-NOT: "-msign-return-address-key"
 
+// RA-INCOMPATIBLE-PTRAUTHRETURNS: the combination of 
'-mbranch-protection=standard' and '-fptrauth-returns' is incompatible
+
 // BTE-OFF-NOT: "-mbranch-target-enforce"
 // BTE-ON: "-mbranch-target-enforce"
 

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..a98a80c85c819
--- /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 misspelled pac-ret hardening specification 
'inexistent'}}
+void
+badvalue4(void) {}

diff  --git a/clang/test/Sema/aarch64-harden-pac-ret-attr-ignored.c 
b/clang/test/Sema/aarch64-harden-pac-ret-attr-ignored.c
new file mode 100644
index 0000000000000..c0a7dd38783ee
--- /dev/null
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr-ignored.c
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple aarch64 -target-cpu generic -target-feature +v8.5a 
-emit-llvm -verify %s -o - | FileCheck %s
+// RUN: %clang_cc1 -triple aarch64 -target-cpu generic -target-feature +v8.5a 
-msign-return-address=all -mharden-pac-ret=none -emit-llvm -verify %s -o - | 
FileCheck %s
+// RUN: %clang_cc1 -triple aarch64 -target-cpu generic -target-feature +v8.5a 
-msign-return-address=all -mharden-pac-ret=load-return-address -emit-llvm 
-verify %s -o - | FileCheck %s
+
+// Invalid harden-pac-ret usage must cause the entire target attribute to be
+// ignored, including its CPU and branch-protection settings. These functions
+// must therefore share the baseline's attributes under each command-line mode.
+void baseline(void) {}
+// CHECK: define{{.*}} void @baseline() #[[#BASELINE:]] {
+
+__attribute__((target("cpu=neoverse-v2,branch-protection=bti,harden-pac-ret=load-return-address")))
+// expected-warning@-1 {{'harden-pac-ret' attribute requires 
'branch-protection=pac-ret'; 'target' attribute ignored}}
+void bti_without_pac_ret(void) {}
+// CHECK: define{{.*}} void @bti_without_pac_ret() #[[#BASELINE]] {
+
+__attribute__((target("cpu=neoverse-v2,harden-pac-ret=load-return-address")))
+// expected-warning@-1 {{'harden-pac-ret' attribute requires 
'branch-protection=pac-ret'; 'target' attribute ignored}}
+void missing_branch_protection(void) {}
+// CHECK: define{{.*}} void @missing_branch_protection() #[[#BASELINE]] {

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..2da3175733b42
--- /dev/null
+++ b/clang/test/Sema/aarch64-harden-pac-ret-attr.c
@@ -0,0 +1,91 @@
+// 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=none")))
+void f1() {}
+// CHECK: define{{.*}} void @f1() #[[#A1:]]
+
+__attribute__ ((target("branch-protection=pac-ret")))
+void f2() {}
+// CHECK: define{{.*}} void @f2() #[[#A2:]]
+
+__attribute__ ((target("branch-protection=pac-ret,harden-pac-ret=none")))
+void f3() {}
+// CHECK: define{{.*}} void @f3() #[[#A2]]
+
+__attribute__ 
((target("branch-protection=pac-ret,harden-pac-ret=load-return-address")))
+void f4() {}
+// CHECK: define{{.*}} void @f4() #[[#A3:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf")))
+void f5() {}
+// CHECK: define{{.*}} void @f5() #[[#A4:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf,harden-pac-ret=none")))
+void f6() {}
+// CHECK: define{{.*}} void @f6() #[[#A4]]
+
+__attribute__ 
((target("branch-protection=pac-ret+leaf,harden-pac-ret=load-return-address")))
+void f7() {}
+// CHECK: define{{.*}} void @f7() #[[#A5:]]
+
+__attribute__ ((target("branch-protection=pac-ret+b-key")))
+void f8() {}
+// CHECK: define{{.*}} void @f8() #[[#A6:]]
+
+__attribute__ ((target("branch-protection=pac-ret+b-key,harden-pac-ret=none")))
+void f9() {}
+// CHECK: define{{.*}} void @f9() #[[#A6]]
+
+__attribute__ 
((target("branch-protection=pac-ret+b-key,harden-pac-ret=load-return-address")))
+void f10() {}
+// CHECK: define{{.*}} void @f10() #[[#A7:]]
+
+__attribute__ ((target("branch-protection=pac-ret+leaf+b-key")))
+void f11() {}
+// CHECK: define{{.*}} void @f11() #[[#A8:]]
+
+__attribute__ 
((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=none")))
+void f12() {}
+// CHECK: define{{.*}} void @f12() #[[#A8]]
+
+__attribute__ 
((target("branch-protection=pac-ret+leaf+b-key,harden-pac-ret=load-return-address")))
+void f13() {}
+// CHECK: define{{.*}} void @f13() #[[#A9:]]
+
+// These check patterns rely on the fact that "sign-return-address-harden" 
appears after "sign-return-address"
+
+// CHECK:     attributes #[[#A1]]
+// CHECK-NOT: "sign-return-address"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     attributes #[[#A2]]
+// CHECK:     "sign-return-address"="non-leaf"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     attributes #[[#A3]]
+// CHECK:     "sign-return-address"="non-leaf"
+// CHECK:     "sign-return-address-harden"="load-return-address"
+// CHECK:     attributes #[[#A4]]
+// CHECK:     "sign-return-address"="all"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     attributes #[[#A5]]
+// CHECK:     "sign-return-address"="all"
+// CHECK:     "sign-return-address-harden"="load-return-address"
+// CHECK:     attributes #[[#A6]]
+// CHECK:     "sign-return-address"="non-leaf"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#A7]]
+// CHECK:     "sign-return-address"="non-leaf"
+// CHECK:     "sign-return-address-harden"="load-return-address"
+// CHECK:     "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#A8]]
+// CHECK:     "sign-return-address"="all"
+// CHECK-NOT: "sign-return-address-harden"
+// CHECK:     "sign-return-address-key"="b_key"
+// CHECK:     attributes #[[#A9]]
+// CHECK:     "sign-return-address"="all"
+// CHECK:     "sign-return-address-harden"="load-return-address"
+// CHECK:     "sign-return-address-key"="b_key"

diff  --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index b369e95263deb..fabce4e6256bd 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -244,6 +244,11 @@ Makes programs 10x faster by doing Special New Thing.
 
 ### Changes to the AArch64 Backend
 
+* Added support for hardening return address signing against PACMAN attacks.
+  Functions with the `"sign-return-address-harden"="load-return-address"`
+  attribute perform a load from the return address before returning, reducing 
the
+  cache side channel used to guess pointer authentication codes.
+
 ### Changes to the AMDGPU Backend
 
 * Replaced `xnack` and `sramecc` target features with `amdgpu.xnack`


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to