https://github.com/DanielKristofKiss updated 
https://github.com/llvm/llvm-project/pull/113368

>From 114455c05ea84fc47fd22a34490d388ba8d5ddc6 Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.k...@arm.com>
Date: Wed, 16 Oct 2024 14:48:25 -0700
Subject: [PATCH 1/3] [libunwind][AArch64] Protect PC within libunwind's
 context.

Libunwind manages the regiser context including the program counter
which is used effectively as return address.
To increase the robustness of libunwind let's protect the stored address
with PAC. Since there is no unwind info for this let's use the A key and
the base address of the context/registers as modifier.
__libunwind_Registers_arm64_jumpto can go anywhere where the given buffer
's PC points to. After this patch it needs a signed PC therefore the context
 is more harder to craft outside of libunwind.

The register value is internal to libunwind and the change is not visible
on the the APIs.
---
 libunwind/CMakeLists.txt               |  46 +++++++
 libunwind/include/__libunwind_config.h |  12 +-
 libunwind/src/Registers.hpp            | 168 ++++++++++++++++++++++++-
 libunwind/src/UnwindRegistersRestore.S |  64 +++++++---
 libunwind/src/UnwindRegistersSave.S    |  51 +++++---
 5 files changed, 298 insertions(+), 43 deletions(-)

diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 3c8499fd33464..390b43b85f400 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -55,6 +55,7 @@ option(LIBUNWIND_USE_FRAME_HEADER_CACHE "Cache frame headers 
for unwinding. Requ
 option(LIBUNWIND_REMEMBER_HEAP_ALLOC "Use heap instead of the stack for 
.cfi_remember_state." OFF)
 option(LIBUNWIND_INSTALL_HEADERS "Install the libunwind headers." ON)
 option(LIBUNWIND_ENABLE_FRAME_APIS "Include libgcc-compatible frame apis." OFF)
+option(LIBUNWIND_ENABLE_REGISTERSET_PROTECTION "Build libunwind with 
protection on the internal registers." ON)
 
 set(LIBUNWIND_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING
     "Define suffix of library directory name (32/64)")
@@ -279,6 +280,51 @@ else()
   endif()
 endif()
 
+if (LIBUNWIND_ENABLE_REGISTERSET_PROTECTION AND (NOT 
LIBUNWIND_ENABLE_CROSS_UNWINDING))
+  # Pointer Authentication Intrinsics are currently only supported with
+  # the FEAT_PAuth, NOP space variant of the instructions are not
+  # supported, enable this feature if libunwind is compiled with 
branch-protection
+  # or pauth intrinsic are availabile.
+
+  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ptrauth_check.cpp" "
+  #if !( defined(__ARM_FEATURE_PAUTH) && \
+         defined(__has_include) && __has_include (<ptrauth.h>) && \
+         defined(__has_feature) &&__has_feature(ptrauth_intrinsics))
+  #  error
+  #endif
+  int main(){}
+  ")
+
+  try_compile(TARGET_SUPPORTS_PTRAUTH
+    "${CMAKE_CURRENT_BINARY_DIR}"
+    "${CMAKE_CURRENT_BINARY_DIR}/ptrauth_check.cpp"
+    COMPILE_DEFINITIONS "${LIBUNWIND_COMPILE_FLAGS}" -fptrauth-intrinsics)
+
+  # Detect if the code is compiled with -mbranch-protection=pac*.
+  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/pac_check.cpp" "
+  #ifndef __ARM_FEATURE_PAC_DEFAULT
+  #  error
+  #endif
+  int main(){}
+  ")
+
+  try_compile(TARGET_SUPPORTS_BRANCH_PROTECTION_PAC
+    "${CMAKE_CURRENT_BINARY_DIR}"
+    "${CMAKE_CURRENT_BINARY_DIR}/pac_check.cpp"
+    COMPILE_DEFINITIONS "${LIBUNWIND_COMPILE_FLAGS}")
+
+  if (TARGET_SUPPORTS_BRANCH_PROTECTION_PAC)
+    add_compile_flags(-D_LIBUNWIND_AARCH64_PC_PROTECTION)
+  endif()
+
+  if (TARGET_SUPPORTS_PTRAUTH)
+    add_compile_flags(-fptrauth-intrinsics)
+    add_compile_flags(-D_LIBUNWIND_PTRAUTH_AVAILABLE)
+    add_compile_flags(-D_LIBUNWIND_AARCH64_PC_PROTECTION)
+  endif()
+
+endif()
+
 # Cross-unwinding
 if (NOT LIBUNWIND_ENABLE_CROSS_UNWINDING)
   add_compile_flags(-D_LIBUNWIND_IS_NATIVE_ONLY)
diff --git a/libunwind/include/__libunwind_config.h 
b/libunwind/include/__libunwind_config.h
index bb7fe4c83a3c1..0c4f8ec1b8a27 100644
--- a/libunwind/include/__libunwind_config.h
+++ b/libunwind/include/__libunwind_config.h
@@ -73,11 +73,19 @@
 #  define _LIBUNWIND_HIGHEST_DWARF_REGISTER 
_LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC
 # elif defined(__aarch64__)
 #  define _LIBUNWIND_TARGET_AARCH64 1
-#  define _LIBUNWIND_CONTEXT_SIZE 66
+#  if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
+#    define _LIBUNWIND_CONTEXT_SIZE 67
+#  else
+#    define _LIBUNWIND_CONTEXT_SIZE 66
+#  endif
 #  if defined(__SEH__)
 #    define _LIBUNWIND_CURSOR_SIZE 164
 #  else
-#    define _LIBUNWIND_CURSOR_SIZE 78
+#    if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
+#      define _LIBUNWIND_CURSOR_SIZE 79
+#    else
+#      define _LIBUNWIND_CURSOR_SIZE 78
+#    endif
 #  endif
 #  define _LIBUNWIND_HIGHEST_DWARF_REGISTER 
_LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64
 # elif defined(__arm__)
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 2c3bfb7e8428a..dedef47ec6190 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -19,6 +19,9 @@
 #include "libunwind.h"
 #include "shadow_stack_unwind.h"
 
+#if defined(_LIBUNWIND_PTRAUTH_AVAILABLE)
+#include <ptrauth.h>
+#endif
 namespace libunwind {
 
 // For emulating 128-bit registers
@@ -1823,9 +1826,127 @@ extern "C" void *__libunwind_shstk_get_jump_target() {
 #endif
 
 class _LIBUNWIND_HIDDEN Registers_arm64 {
+  struct GPRs;
+
+private:
+  /// The program counter is used effectively as a return address
+  /// when the context is restored therefore protect it with PAC.
+  /// The base address of the context is used with the A key for
+  /// authentication and signing. Return address authentication is
+  /// still managed according to the unwind info. In some cases
+  /// the LR contains significant bits in the space for the PAC bits the
+  /// value of the PC is stored in 2 halfs and each signed.
+  inline uint64_t getDiscriminator() const {
+    return reinterpret_cast<uint64_t>(this);
+  }
+#if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
+#if defined(_LIBUNWIND_PTRAUTH_AVAILABLE)
+/// Use Pointer Authentication Intrinsics when available.
+#define __libunwind_ptrauth_auth_data(__value, __key, __discriminator)         
\
+  ptrauth_auth_data(__value, __key, __discriminator)
+#define __libunwind_ptrauth_auth_and_resign(pointer, oldKey, oldDiscriminator, 
\
+                                            newKey, newDiscriminator)          
\
+  ptrauth_auth_and_resign(pointer, oldKey, oldDiscriminator, newKey,           
\
+                          newDiscriminator)
+#define __libunwind_ptrauth_sign_unauthenticated(__value, __key, __data)       
\
+  ptrauth_sign_unauthenticated(__value, __key, __data)
+#else // !_LIBUNWIND_PTRAUTH_AVAILABLE
+  typedef enum {
+    ptrauth_key_asia = 0,
+  } ptrauth_key;
+  /// Using only the NOP space compatible instructions. FPAC might not be
+  /// available on the target so a manual check is added.
+  inline void *__libunwind_ptrauth_strip(void *__value,
+                                         ptrauth_key __key) const {
+    assert(__key == ptrauth_key_asia && "Only A key is supported");
+    void *__return = 0;
+    asm("mov   x30, %[__value]      \r\n"
+        "hint  0x7                  \r\n" // xpaclri
+        "mov   %[__return], x30     \r\n"
+        : [__return] "+r"(__return)
+        : [__value] "r"(__value)
+        : "x30");
+    return __return;
+  }
+
+  inline void *__libunwind_ptrauth_auth_data(void *__value, ptrauth_key __key,
+                                             uint64_t __discriminator) const {
+    assert(__key == ptrauth_key_asia && "Only A key is supported");
+    register void *x17 __asm("x17") = __value;
+    register uintptr_t x16 __asm("x16") = __discriminator;
+    asm("hint  0xc" // autia1716
+        : "+r"(x17)
+        : "r"(x16)
+        :);
+    if (x17 != __libunwind_ptrauth_strip(__value, __key))
+      _LIBUNWIND_ABORT("ptrauth authentication failure");
+    return x17;
+  }
+
+  inline void *
+  __libunwind_ptrauth_sign_unauthenticated(void *__value, ptrauth_key __key,
+                                           uint64_t __discriminator) const {
+    assert(__key == ptrauth_key_asia && "Only A key is supported");
+    register void *x17 __asm("x17") = __value;
+    register uint64_t x16 __asm("x16") = __discriminator;
+    asm("hint 0x8" : "+r"(x17) : "r"(x16));
+    return x17;
+  }
+
+  inline void *__libunwind_ptrauth_auth_and_resign(
+      void *pointer, ptrauth_key oldKey, uint64_t oldDiscriminator,
+      ptrauth_key newKey, uint64_t newDiscriminator) const {
+    return __libunwind_ptrauth_sign_unauthenticated(
+        __libunwind_ptrauth_auth_data(pointer, oldKey, oldDiscriminator),
+        newKey, newDiscriminator);
+  }
+#endif
+  // Authenticate the currently stored PC and return it's raw value.
+  inline uint64_t authPC(const struct GPRs *gprs,
+                         uint64_t discriminator) const {
+    uint64_t lower = (uint64_t)__libunwind_ptrauth_auth_data(
+        (void *)gprs->__pc, ptrauth_key_asia, discriminator);
+    uint64_t upper = (uint64_t)__libunwind_ptrauth_auth_data(
+        (void *)gprs->__pc2, ptrauth_key_asia, discriminator);
+    return (upper << 32) | lower;
+  }
+
+  // Sign and store the new PC.
+  inline void updatePC(uint64_t value) {
+    _registers.__pc = (uint64_t)__libunwind_ptrauth_sign_unauthenticated(
+        (void *)(value & (((uint64_t)~0) >> 32)), ptrauth_key_asia,
+        getDiscriminator());
+    _registers.__pc2 = (uint64_t)__libunwind_ptrauth_sign_unauthenticated(
+        (void *)(value >> 32), ptrauth_key_asia, getDiscriminator());
+  }
+
+  // Update the signature on the current PC.
+  inline void resignPC(uint64_t oldDiscriminator) {
+    _registers.__pc = (uint64_t)__libunwind_ptrauth_auth_and_resign(
+        (void *)_registers.__pc, ptrauth_key_asia, oldDiscriminator,
+        ptrauth_key_asia, getDiscriminator());
+    _registers.__pc2 = (uint64_t)__libunwind_ptrauth_auth_and_resign(
+        (void *)_registers.__pc2, ptrauth_key_asia, oldDiscriminator,
+        ptrauth_key_asia, getDiscriminator());
+  }
+#else //! defined(_LIBUNWIND_AARCH64_PC_PROTECTION))
+  // Remote unwinding is not supported by this protection.
+  inline uint64_t authPC(const struct GPRs *gprs,
+                         const uint64_t discriminator) const {
+    (void)discriminator;
+    return gprs->__pc;
+  }
+  inline void updatePC(const uint64_t value) { _registers.__pc = value; }
+  inline void resignPC(uint64_t oldDiscriminator) { (void)oldDiscriminator; }
+#endif
+
 public:
   Registers_arm64();
   Registers_arm64(const void *registers);
+  Registers_arm64(const Registers_arm64 &other);
+  Registers_arm64(const Registers_arm64 &&other) = delete;
+  Registers_arm64 &operator=(const Registers_arm64 &other);
+  Registers_arm64 &operator=(Registers_arm64 &&other) = delete;
 
   bool        validRegister(int num) const;
   uint64_t    getRegister(int num) const;
@@ -1845,8 +1966,14 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
 
   uint64_t  getSP() const         { return _registers.__sp; }
   void      setSP(uint64_t value) { _registers.__sp = value; }
-  uint64_t  getIP() const         { return _registers.__pc; }
-  void      setIP(uint64_t value) { _registers.__pc = value; }
+  uint64_t getIP() const { return authPC(&_registers, getDiscriminator()); }
+  void setIP(uint64_t value) {
+    // First authenticate the current value of the IP to ensure the context
+    // is still valid. This also ensure the setIP can't be used for signing
+    // arbitrary values.
+    authPC(&_registers, getDiscriminator());
+    updatePC(value);
+  }
   uint64_t  getFP() const         { return _registers.__fp; }
   void      setFP(uint64_t value) { _registers.__fp = value; }
 
@@ -1858,12 +1985,15 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
     uint64_t __sp;    // Stack pointer x31
     uint64_t __pc;    // Program counter
     uint64_t __ra_sign_state; // RA sign state register
+#if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
+    uint64_t __pc2; // PC's signed upper part
+#endif
   };
 
   GPRs    _registers;
   double  _vectorHalfRegisters[32];
-  // Currently only the lower double in 128-bit vectore registers
-  // is perserved during unwinding.  We could define new register
+  // Currently only the lower double in 128-bit vector registers
+  // is preserved during unwinding.  We could define new register
   // numbers (> 96) which mean whole vector registers, then this
   // struct would need to change to contain whole vector registers.
 };
@@ -1872,8 +2002,15 @@ inline Registers_arm64::Registers_arm64(const void 
*registers) {
   static_assert((check_fit<Registers_arm64, unw_context_t>::does_fit),
                 "arm64 registers do not fit into unw_context_t");
   memcpy(&_registers, registers, sizeof(_registers));
+#if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
+  static_assert(sizeof(GPRs) == 0x118,
+                "expected VFP registers to be at offset 280");
+#else
   static_assert(sizeof(GPRs) == 0x110,
                 "expected VFP registers to be at offset 272");
+#endif
+  // getcontext signs the PC with the base address of the context.
+  resignPC(reinterpret_cast<uint64_t>(registers));
   memcpy(_vectorHalfRegisters,
          static_cast<const uint8_t *>(registers) + sizeof(GPRs),
          sizeof(_vectorHalfRegisters));
@@ -1882,6 +2019,25 @@ inline Registers_arm64::Registers_arm64(const void 
*registers) {
 inline Registers_arm64::Registers_arm64() {
   memset(&_registers, 0, sizeof(_registers));
   memset(&_vectorHalfRegisters, 0, sizeof(_vectorHalfRegisters));
+  // We don't know the value of the PC but let's sign it to indicate we have a
+  // valid register set.
+  updatePC(0);
+}
+
+inline Registers_arm64::Registers_arm64(const Registers_arm64 &other) {
+  memcpy(&_registers, &other._registers, sizeof(_registers));
+  memcpy(&_vectorHalfRegisters, &other._vectorHalfRegisters,
+         sizeof(_vectorHalfRegisters));
+  resignPC(other.getDiscriminator());
+}
+
+inline Registers_arm64 &
+Registers_arm64::operator=(const Registers_arm64 &other) {
+  memcpy(&_registers, &other._registers, sizeof(_registers));
+  memcpy(&_vectorHalfRegisters, &other._vectorHalfRegisters,
+         sizeof(_vectorHalfRegisters));
+  resignPC(other.getDiscriminator());
+  return *this;
 }
 
 inline bool Registers_arm64::validRegister(int regNum) const {
@@ -1902,7 +2058,7 @@ inline bool Registers_arm64::validRegister(int regNum) 
const {
 
 inline uint64_t Registers_arm64::getRegister(int regNum) const {
   if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
-    return _registers.__pc;
+    return getIP();
   if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
     return _registers.__sp;
   if (regNum == UNW_AARCH64_RA_SIGN_STATE)
@@ -1918,7 +2074,7 @@ inline uint64_t Registers_arm64::getRegister(int regNum) 
const {
 
 inline void Registers_arm64::setRegister(int regNum, uint64_t value) {
   if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
-    _registers.__pc = value;
+    setIP(value);
   else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
     _registers.__sp = value;
   else if (regNum == UNW_AARCH64_RA_SIGN_STATE)
diff --git a/libunwind/src/UnwindRegistersRestore.S 
b/libunwind/src/UnwindRegistersRestore.S
index 1702d016c368b..37c30fad2d683 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -657,25 +657,49 @@ 
DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
   ldp    x24,x25, [x0, #0x0C0]
   ldp    x26,x27, [x0, #0x0D0]
   ldp    x28,x29, [x0, #0x0E0]
+#if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
+#define __VOFFSET 0x118
+  // Authenticate return address with the address of the context.
+  ldr    x30,     [x0, #0x100]  // __pc
+  mov    x17, x30
+  mov    x16,  x0
+  hint   0xc      // autia1716
+  xpaclri
+  cmp    x17, x30
+  b.ne   LauthError
+  mov    x1, x17
+  ldr    x30,     [x0, #0x110]  // __pc2
+  mov    x17, x30
+  hint   0xc // autia1716
+  xpaclri
+  cmp    x17, x30
+  b.ne   LauthError
+  lsl    x17, x17, 32
+  orr    x30, x17, x1
+  mov    x16, xzr
+  mov    x17, xzr
+#else
+#define __VOFFSET 0x110
   ldr    x30,     [x0, #0x100]  // restore pc into lr
+#endif
 #if defined(__ARM_FP) && __ARM_FP != 0
-  ldp    d0, d1,  [x0, #0x110]
-  ldp    d2, d3,  [x0, #0x120]
-  ldp    d4, d5,  [x0, #0x130]
-  ldp    d6, d7,  [x0, #0x140]
-  ldp    d8, d9,  [x0, #0x150]
-  ldp    d10,d11, [x0, #0x160]
-  ldp    d12,d13, [x0, #0x170]
-  ldp    d14,d15, [x0, #0x180]
-  ldp    d16,d17, [x0, #0x190]
-  ldp    d18,d19, [x0, #0x1A0]
-  ldp    d20,d21, [x0, #0x1B0]
-  ldp    d22,d23, [x0, #0x1C0]
-  ldp    d24,d25, [x0, #0x1D0]
-  ldp    d26,d27, [x0, #0x1E0]
-  ldp    d28,d29, [x0, #0x1F0]
-  ldr    d30,     [x0, #0x200]
-  ldr    d31,     [x0, #0x208]
+  ldp    d0, d1,  [x0, #(__VOFFSET + 0x0)]
+  ldp    d2, d3,  [x0, #(__VOFFSET + 0x10)]
+  ldp    d4, d5,  [x0, #(__VOFFSET + 0x20)]
+  ldp    d6, d7,  [x0, #(__VOFFSET + 0x30)]
+  ldp    d8, d9,  [x0, #(__VOFFSET + 0x40)]
+  ldp    d10,d11, [x0, #(__VOFFSET + 0x50)]
+  ldp    d12,d13, [x0, #(__VOFFSET + 0x60)]
+  ldp    d14,d15, [x0, #(__VOFFSET + 0x70)]
+  ldp    d16,d17, [x0, #(__VOFFSET + 0x80)]
+  ldp    d18,d19, [x0, #(__VOFFSET + 0x90)]
+  ldp    d20,d21, [x0, #(__VOFFSET + 0xA0)]
+  ldp    d22,d23, [x0, #(__VOFFSET + 0xB0)]
+  ldp    d24,d25, [x0, #(__VOFFSET + 0xC0)]
+  ldp    d26,d27, [x0, #(__VOFFSET + 0xD0)]
+  ldp    d28,d29, [x0, #(__VOFFSET + 0xE0)]
+  ldr    d30,     [x0, #(__VOFFSET + 0xF0)]
+  ldr    d31,     [x0, #(__VOFFSET + 0xF8)]
 #endif
   // Finally, restore sp. This must be done after the last read from the
   // context struct, because it is allocated on the stack, and an exception
@@ -695,7 +719,11 @@ 
DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
 Lnogcs:
 #endif
   ret    x30                    // jump to pc
-
+#if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
+LauthError:
+  mov x30, xzr
+  ret x30
+#endif
 #elif defined(__arm__) && !defined(__APPLE__)
 
 #if !defined(__ARM_ARCH_ISA_ARM)
diff --git a/libunwind/src/UnwindRegistersSave.S 
b/libunwind/src/UnwindRegistersSave.S
index a489a8ba6df15..3b0a56df7cc37 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -744,26 +744,43 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
   str    x30,     [x0, #0x0F0]
   mov    x1,sp
   str    x1,      [x0, #0x0F8]
+#if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
+#define __VOFFSET 0x118
+  // Sign the return address as pc with the address of the context
+  // buttom and the top word signed and stored sepearetly.
+  mov    w17, w30                 // lower part of the PC
+  mov    x16, x0                  // sign with the address of the context
+  hint   0x8                      // pacia1716
+  str    x17,     [x0, #0x100]    // store to __pc
+  mov    x17, x30                 // upper part of the PC
+  lsr    x17, x17, 32
+  hint   0x8                      // pacia1716
+  str    x17,     [x0, #0x110]    // store to __pc2
+  mov    x17, xzr                 // let's not leak PC or the discriminator
+  mov    x16, xzr
+#else
+#define __VOFFSET 0x110
   str    x30,     [x0, #0x100]    // store return address as pc
+#endif
   // skip cpsr
 #if defined(__ARM_FP) && __ARM_FP != 0
-  stp    d0, d1,  [x0, #0x110]
-  stp    d2, d3,  [x0, #0x120]
-  stp    d4, d5,  [x0, #0x130]
-  stp    d6, d7,  [x0, #0x140]
-  stp    d8, d9,  [x0, #0x150]
-  stp    d10,d11, [x0, #0x160]
-  stp    d12,d13, [x0, #0x170]
-  stp    d14,d15, [x0, #0x180]
-  stp    d16,d17, [x0, #0x190]
-  stp    d18,d19, [x0, #0x1A0]
-  stp    d20,d21, [x0, #0x1B0]
-  stp    d22,d23, [x0, #0x1C0]
-  stp    d24,d25, [x0, #0x1D0]
-  stp    d26,d27, [x0, #0x1E0]
-  stp    d28,d29, [x0, #0x1F0]
-  str    d30,     [x0, #0x200]
-  str    d31,     [x0, #0x208]
+  stp    d0, d1,  [x0, #(__VOFFSET + 0x0)]
+  stp    d2, d3,  [x0, #(__VOFFSET + 0x10)]
+  stp    d4, d5,  [x0, #(__VOFFSET + 0x20)]
+  stp    d6, d7,  [x0, #(__VOFFSET + 0x30)]
+  stp    d8, d9,  [x0, #(__VOFFSET + 0x40)]
+  stp    d10,d11, [x0, #(__VOFFSET + 0x50)]
+  stp    d12,d13, [x0, #(__VOFFSET + 0x60)]
+  stp    d14,d15, [x0, #(__VOFFSET + 0x70)]
+  stp    d16,d17, [x0, #(__VOFFSET + 0x80)]
+  stp    d18,d19, [x0, #(__VOFFSET + 0x90)]
+  stp    d20,d21, [x0, #(__VOFFSET + 0xA0)]
+  stp    d22,d23, [x0, #(__VOFFSET + 0xB0)]
+  stp    d24,d25, [x0, #(__VOFFSET + 0xC0)]
+  stp    d26,d27, [x0, #(__VOFFSET + 0xD0)]
+  stp    d28,d29, [x0, #(__VOFFSET + 0xE0)]
+  str    d30,     [x0, #(__VOFFSET + 0xF0)]
+  str    d31,     [x0, #(__VOFFSET + 0xF8)]
 #endif
   mov    x0, #0                   // return UNW_ESUCCESS
   ret

>From b40594b7c781251cb20b7a775dfbaf6df75650bd Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.k...@arm.com>
Date: Fri, 6 Jun 2025 18:55:05 +0200
Subject: [PATCH 2/3] Improve the signing authentication scheme.

Added a descriminator to the buffer.
Signed the second part of the PC with the first part.
misc fix:wq use hint instead of xplaclri
---
 libunwind/src/Registers.hpp            | 55 ++++++++++++++++++--------
 libunwind/src/UnwindRegistersRestore.S | 24 +++++------
 libunwind/src/UnwindRegistersSave.S    |  5 ++-
 3 files changed, 54 insertions(+), 30 deletions(-)

diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index dedef47ec6190..dec6b1fa1ff18 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1829,19 +1829,18 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
   struct GPRs;
 
 private:
-  /// The program counter is used effectively as a return address
-  /// when the context is restored therefore protect it with PAC.
-  /// The base address of the context is used with the A key for
-  /// authentication and signing. Return address authentication is
-  /// still managed according to the unwind info. In some cases
-  /// the LR contains significant bits in the space for the PAC bits the
-  /// value of the PC is stored in 2 halfs and each signed.
-  inline uint64_t getDiscriminator() const {
-    return reinterpret_cast<uint64_t>(this);
-  }
 #if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
+  /// The program counter is effectively used as a return address when the
+  /// context is restored; therefore, it should be protected with PAC to 
prevent
+  /// arbitrary returns. The base address of the context is blended with a
+  /// discriminator using the A key for authentication and signing. Return
+  /// address authentication is still managed according to the unwind
+  /// information. In some cases, the LR contains significant bits in the space
+  /// reserved for PAC bits, so the value of the PC is stored in two halves.
+  ///  The second half is signed using the first half as a discriminator to 
bind
+  ///  the two halves together.
 #if defined(_LIBUNWIND_PTRAUTH_AVAILABLE)
-/// Use Pointer Authentication Intrinsics when available.
+  /// Use Pointer Authentication Intrinsics when available.
 #define __libunwind_ptrauth_auth_data(__value, __key, __discriminator)         
\
   ptrauth_auth_data(__value, __key, __discriminator)
 #define __libunwind_ptrauth_auth_and_resign(pointer, oldKey, oldDiscriminator, 
\
@@ -1850,6 +1849,8 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
                           newDiscriminator)
 #define __libunwind_ptrauth_sign_unauthenticated(__value, __key, __data)       
\
   ptrauth_sign_unauthenticated(__value, __key, __data)
+#define __libunwind_ptrauth_blend_discriminator(__ptr, __data)                 
\
+  ptrauth_blend_discriminator(__ptr, __data)
 #else // !_LIBUNWIND_PTRAUTH_AVAILABLE
   typedef enum {
     ptrauth_key_asia = 0,
@@ -1900,14 +1901,21 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
         __libunwind_ptrauth_auth_data(pointer, oldKey, oldDiscriminator),
         newKey, newDiscriminator);
   }
+#define __libunwind_ptrauth_blend_discriminator(__ptr, __data)                 
\
+  ptrauth_blend_discriminator(__ptr, __data)
 #endif
+  inline uint64_t
+  __libunwind_ptrauth_blend_discriminator(uint64_t __ptr,
+                                          uint16_t __data) const {
+    return (__ptr & (~0 >> 16)) | ((uint64_t)__data << 48);
+  }
   // Authenticate the currently stored PC and return it's raw value.
   inline uint64_t authPC(const struct GPRs *gprs,
                          uint64_t discriminator) const {
+    uint64_t upper = (uint64_t)__libunwind_ptrauth_auth_data(
+        (void *)gprs->__pc2, ptrauth_key_asia, gprs->__pc);
     uint64_t lower = (uint64_t)__libunwind_ptrauth_auth_data(
         (void *)gprs->__pc, ptrauth_key_asia, discriminator);
-    uint64_t upper = (uint64_t)__libunwind_ptrauth_auth_data(
-        (void *)gprs->__pc2, ptrauth_key_asia, discriminator);
     return (upper << 32) | lower;
   }
 
@@ -1917,19 +1925,26 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
         (void *)(value & (((uint64_t)~0) >> 32)), ptrauth_key_asia,
         getDiscriminator());
     _registers.__pc2 = (uint64_t)__libunwind_ptrauth_sign_unauthenticated(
-        (void *)(value >> 32), ptrauth_key_asia, getDiscriminator());
+        (void *)(value >> 32), ptrauth_key_asia, _registers.__pc);
   }
 
   // Update the signature on the current PC.
   inline void resignPC(uint64_t oldDiscriminator) {
+    uint64_t old_signed_pc = _registers.__pc;
     _registers.__pc = (uint64_t)__libunwind_ptrauth_auth_and_resign(
         (void *)_registers.__pc, ptrauth_key_asia, oldDiscriminator,
         ptrauth_key_asia, getDiscriminator());
     _registers.__pc2 = (uint64_t)__libunwind_ptrauth_auth_and_resign(
-        (void *)_registers.__pc2, ptrauth_key_asia, oldDiscriminator,
-        ptrauth_key_asia, getDiscriminator());
+        (void *)_registers.__pc2, ptrauth_key_asia, old_signed_pc,
+        ptrauth_key_asia, _registers.__pc);
   }
 #else //! defined(_LIBUNWIND_AARCH64_PC_PROTECTION))
+  inline uint64_t
+  __libunwind_ptrauth_blend_discriminator(uint64_t __ptr,
+                                          uint16_t __data) const {
+    (void)__data;
+    return __ptr;
+  }
   // Remote unwinding is not supported by this protection.
   inline uint64_t authPC(const struct GPRs *gprs,
                          const uint64_t discriminator) const {
@@ -1940,6 +1955,11 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
   inline void resignPC(uint64_t oldDiscriminator) { (void)oldDiscriminator; }
 #endif
 
+  inline uint64_t getDiscriminator() const {
+    return __libunwind_ptrauth_blend_discriminator(
+        reinterpret_cast<uint64_t>(this), 0xface);
+  }
+
 public:
   Registers_arm64();
   Registers_arm64(const void *registers);
@@ -2010,7 +2030,8 @@ inline Registers_arm64::Registers_arm64(const void 
*registers) {
                 "expected VFP registers to be at offset 272");
 #endif
   // getcontext signs the PC with the base address of the context.
-  resignPC(reinterpret_cast<uint64_t>(registers));
+  resignPC(__libunwind_ptrauth_blend_discriminator(
+      reinterpret_cast<uint64_t>(registers), 0xface));
   memcpy(_vectorHalfRegisters,
          static_cast<const uint8_t *>(registers) + sizeof(GPRs),
          sizeof(_vectorHalfRegisters));
diff --git a/libunwind/src/UnwindRegistersRestore.S 
b/libunwind/src/UnwindRegistersRestore.S
index 37c30fad2d683..fb6e6fdd7893c 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -659,24 +659,26 @@ 
DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
   ldp    x28,x29, [x0, #0x0E0]
 #if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
 #define __VOFFSET 0x118
-  // Authenticate return address with the address of the context.
-  ldr    x30,     [x0, #0x100]  // __pc
+  // See the description in Registers_arm64
+  ldr    x30,     [x0, #0x100]    // __pc
   mov    x17, x30
-  mov    x16,  x0
-  hint   0xc      // autia1716
-  xpaclri
-  cmp    x17, x30
+  mov    x16,  x0                 // auth with the buffer's address
+  movk   x16, #0xface, lsl #48    // blinded with a constant
+  hint   0xc                      // autia1716
+  hint   0x7                      // xpaclri
+  cmp    x17, x30                 // check if the auth failed
   b.ne   LauthError
   mov    x1, x17
-  ldr    x30,     [x0, #0x110]  // __pc2
+  ldr    x30,     [x0, #0x110]    // __pc2
   mov    x17, x30
-  hint   0xc // autia1716
-  xpaclri
-  cmp    x17, x30
+  ldr    x16,     [x0, #0x100]    // Auth the second part with the signed 
first part
+  hint   0xc                      // autia1716
+  hint   0x7                      // xpaclri
+  cmp    x17, x30                 // check if the auth failed
   b.ne   LauthError
   lsl    x17, x17, 32
   orr    x30, x17, x1
-  mov    x16, xzr
+  mov    x16, xzr                 // clearup registers
   mov    x17, xzr
 #else
 #define __VOFFSET 0x110
diff --git a/libunwind/src/UnwindRegistersSave.S 
b/libunwind/src/UnwindRegistersSave.S
index 3b0a56df7cc37..221dc145dbc03 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -746,11 +746,12 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
   str    x1,      [x0, #0x0F8]
 #if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
 #define __VOFFSET 0x118
-  // Sign the return address as pc with the address of the context
-  // buttom and the top word signed and stored sepearetly.
+  // See the description in Registers_arm64
   mov    w17, w30                 // lower part of the PC
   mov    x16, x0                  // sign with the address of the context
+  movk   x16, #0xface, lsl #48    // blinded with a constant
   hint   0x8                      // pacia1716
+  mov    x16, x17                 // sign the second part with the first part
   str    x17,     [x0, #0x100]    // store to __pc
   mov    x17, x30                 // upper part of the PC
   lsr    x17, x17, 32

>From 0d10ee43046215c54ac93e0ce445738e482dd076 Mon Sep 17 00:00:00 2001
From: Daniel Kiss <daniel.k...@arm.com>
Date: Fri, 6 Jun 2025 21:39:29 +0200
Subject: [PATCH 3/3] add always inline attribute to the update_pc.

also move define the descriminator instead of magic value.
---
 libunwind/src/Registers.hpp | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index dec6b1fa1ff18..a7ddf6a61e35e 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1829,6 +1829,8 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
   struct GPRs;
 
 private:
+#define PAC_DISCRIMINATOR ((uint16_t)0xFACEU)
+
 #if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
   /// The program counter is effectively used as a return address when the
   /// context is restored; therefore, it should be protected with PAC to 
prevent
@@ -1901,14 +1903,13 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
         __libunwind_ptrauth_auth_data(pointer, oldKey, oldDiscriminator),
         newKey, newDiscriminator);
   }
-#define __libunwind_ptrauth_blend_discriminator(__ptr, __data)                 
\
-  ptrauth_blend_discriminator(__ptr, __data)
-#endif
   inline uint64_t
-  __libunwind_ptrauth_blend_discriminator(uint64_t __ptr,
+  __libunwind_ptrauth_blend_discriminator(const void *__ptr,
                                           uint16_t __data) const {
-    return (__ptr & (~0 >> 16)) | ((uint64_t)__data << 48);
+    return (reinterpret_cast<uint64_t>(__ptr) & (~0 >> 16)) |
+           ((uint64_t)__data << 48);
   }
+#endif
   // Authenticate the currently stored PC and return it's raw value.
   inline uint64_t authPC(const struct GPRs *gprs,
                          uint64_t discriminator) const {
@@ -1920,7 +1921,7 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
   }
 
   // Sign and store the new PC.
-  inline void updatePC(uint64_t value) {
+  inline void updatePC(uint64_t value) __attribute__((always_inline)) {
     _registers.__pc = (uint64_t)__libunwind_ptrauth_sign_unauthenticated(
         (void *)(value & (((uint64_t)~0) >> 32)), ptrauth_key_asia,
         getDiscriminator());
@@ -1940,7 +1941,7 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
   }
 #else //! defined(_LIBUNWIND_AARCH64_PC_PROTECTION))
   inline uint64_t
-  __libunwind_ptrauth_blend_discriminator(uint64_t __ptr,
+  __libunwind_ptrauth_blend_discriminator(const void *__ptr,
                                           uint16_t __data) const {
     (void)__data;
     return __ptr;
@@ -1956,8 +1957,7 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
 #endif
 
   inline uint64_t getDiscriminator() const {
-    return __libunwind_ptrauth_blend_discriminator(
-        reinterpret_cast<uint64_t>(this), 0xface);
+    return __libunwind_ptrauth_blend_discriminator(this, PAC_DISCRIMINATOR);
   }
 
 public:
@@ -2030,8 +2030,8 @@ inline Registers_arm64::Registers_arm64(const void 
*registers) {
                 "expected VFP registers to be at offset 272");
 #endif
   // getcontext signs the PC with the base address of the context.
-  resignPC(__libunwind_ptrauth_blend_discriminator(
-      reinterpret_cast<uint64_t>(registers), 0xface));
+  resignPC(
+      __libunwind_ptrauth_blend_discriminator(registers, PAC_DISCRIMINATOR));
   memcpy(_vectorHalfRegisters,
          static_cast<const uint8_t *>(registers) + sizeof(GPRs),
          sizeof(_vectorHalfRegisters));

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to