1919#include " libunwind.h"
2020#include " shadow_stack_unwind.h"
2121
22+ #if defined(_LIBUNWIND_PTRAUTH_AVAILABLE)
23+ #include < ptrauth.h>
24+ #endif
2225namespace libunwind {
2326
2427// For emulating 128-bit registers
@@ -1823,9 +1826,127 @@ extern "C" void *__libunwind_shstk_get_jump_target() {
18231826#endif
18241827
18251828class _LIBUNWIND_HIDDEN Registers_arm64 {
1829+ struct GPRs ;
1830+
1831+ private:
1832+ // / The program counter is used effectively as a return address
1833+ // / when the context is restored therefore protect it with PAC.
1834+ // / The base address of the context is used with the A key for
1835+ // / authentication and signing. Return address authentication is
1836+ // / still managed according to the unwind info. In some cases
1837+ // / the LR contains significant bits in the space for the PAC bits the
1838+ // / value of the PC is stored in 2 halfs and each signed.
1839+ inline uint64_t getDiscriminator () const {
1840+ return reinterpret_cast <uint64_t >(this );
1841+ }
1842+ #if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
1843+ #if defined(_LIBUNWIND_PTRAUTH_AVAILABLE)
1844+ // / Use Pointer Authentication Intrinsics when available.
1845+ #define __libunwind_ptrauth_auth_data (__value, __key, __discriminator ) \
1846+ ptrauth_auth_data (__value, __key, __discriminator)
1847+ #define __libunwind_ptrauth_auth_and_resign (pointer, oldKey, oldDiscriminator, \
1848+ newKey, newDiscriminator) \
1849+ ptrauth_auth_and_resign (pointer, oldKey, oldDiscriminator, newKey, \
1850+ newDiscriminator)
1851+ #define __libunwind_ptrauth_sign_unauthenticated (__value, __key, __data ) \
1852+ ptrauth_sign_unauthenticated (__value, __key, __data)
1853+ #else // !_LIBUNWIND_PTRAUTH_AVAILABLE
1854+ typedef enum {
1855+ ptrauth_key_asia = 0 ,
1856+ } ptrauth_key;
1857+ // / Using only the NOP space compatible instructions. FPAC might not be
1858+ // / available on the target so a manual check is added.
1859+ inline void *__libunwind_ptrauth_strip (void *__value,
1860+ ptrauth_key __key) const {
1861+ assert (__key == ptrauth_key_asia && " Only A key is supported" );
1862+ void *__return = 0 ;
1863+ asm (" mov x30, %[__value] \r\n "
1864+ " hint 0x7 \r\n " // xpaclri
1865+ " mov %[__return], x30 \r\n "
1866+ : [__return] " +r" (__return)
1867+ : [__value] " r" (__value)
1868+ : " x30" );
1869+ return __return;
1870+ }
1871+
1872+ inline void *__libunwind_ptrauth_auth_data (void *__value, ptrauth_key __key,
1873+ uint64_t __discriminator) const {
1874+ assert (__key == ptrauth_key_asia && " Only A key is supported" );
1875+ register void *x17 __asm (" x17" ) = __value;
1876+ register uintptr_t x16 __asm (" x16" ) = __discriminator;
1877+ asm (" hint 0xc" // autia1716
1878+ : " +r" (x17)
1879+ : " r" (x16)
1880+ :);
1881+ if (x17 != __libunwind_ptrauth_strip (__value, __key))
1882+ _LIBUNWIND_ABORT (" ptrauth authentication failure" );
1883+ return x17;
1884+ }
1885+
1886+ inline void *
1887+ __libunwind_ptrauth_sign_unauthenticated (void *__value, ptrauth_key __key,
1888+ uint64_t __discriminator) const {
1889+ assert (__key == ptrauth_key_asia && " Only A key is supported" );
1890+ register void *x17 __asm (" x17" ) = __value;
1891+ register uint64_t x16 __asm (" x16" ) = __discriminator;
1892+ asm (" hint 0x8" : " +r" (x17) : " r" (x16));
1893+ return x17;
1894+ }
1895+
1896+ inline void *__libunwind_ptrauth_auth_and_resign (
1897+ void *pointer, ptrauth_key oldKey, uint64_t oldDiscriminator,
1898+ ptrauth_key newKey, uint64_t newDiscriminator) const {
1899+ return __libunwind_ptrauth_sign_unauthenticated (
1900+ __libunwind_ptrauth_auth_data (pointer, oldKey, oldDiscriminator),
1901+ newKey, newDiscriminator);
1902+ }
1903+ #endif
1904+ // Authenticate the currently stored PC and return it's raw value.
1905+ inline uint64_t authPC (const struct GPRs *gprs,
1906+ uint64_t discriminator) const {
1907+ uint64_t lower = (uint64_t )__libunwind_ptrauth_auth_data (
1908+ (void *)gprs->__pc , ptrauth_key_asia, discriminator);
1909+ uint64_t upper = (uint64_t )__libunwind_ptrauth_auth_data (
1910+ (void *)gprs->__pc2 , ptrauth_key_asia, discriminator);
1911+ return (upper << 32 ) | lower;
1912+ }
1913+
1914+ // Sign and store the new PC.
1915+ inline void updatePC (uint64_t value) {
1916+ _registers.__pc = (uint64_t )__libunwind_ptrauth_sign_unauthenticated (
1917+ (void *)(value & (((uint64_t )~0 ) >> 32 )), ptrauth_key_asia,
1918+ getDiscriminator ());
1919+ _registers.__pc2 = (uint64_t )__libunwind_ptrauth_sign_unauthenticated (
1920+ (void *)(value >> 32 ), ptrauth_key_asia, getDiscriminator ());
1921+ }
1922+
1923+ // Update the signature on the current PC.
1924+ inline void resignPC (uint64_t oldDiscriminator) {
1925+ _registers.__pc = (uint64_t )__libunwind_ptrauth_auth_and_resign (
1926+ (void *)_registers.__pc , ptrauth_key_asia, oldDiscriminator,
1927+ ptrauth_key_asia, getDiscriminator ());
1928+ _registers.__pc2 = (uint64_t )__libunwind_ptrauth_auth_and_resign (
1929+ (void *)_registers.__pc2 , ptrauth_key_asia, oldDiscriminator,
1930+ ptrauth_key_asia, getDiscriminator ());
1931+ }
1932+ #else // ! defined(_LIBUNWIND_AARCH64_PC_PROTECTION))
1933+ // Remote unwinding is not supported by this protection.
1934+ inline uint64_t authPC (const struct GPRs *gprs,
1935+ const uint64_t discriminator) const {
1936+ (void )discriminator;
1937+ return gprs->__pc ;
1938+ }
1939+ inline void updatePC (const uint64_t value) { _registers.__pc = value; }
1940+ inline void resignPC (uint64_t oldDiscriminator) { (void )oldDiscriminator; }
1941+ #endif
1942+
18261943public:
18271944 Registers_arm64 ();
18281945 Registers_arm64 (const void *registers);
1946+ Registers_arm64 (const Registers_arm64 &other);
1947+ Registers_arm64 (const Registers_arm64 &&other) = delete ;
1948+ Registers_arm64 &operator =(const Registers_arm64 &other);
1949+ Registers_arm64 &operator =(Registers_arm64 &&other) = delete ;
18291950
18301951 bool validRegister (int num) const ;
18311952 uint64_t getRegister (int num) const ;
@@ -1845,8 +1966,14 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
18451966
18461967 uint64_t getSP () const { return _registers.__sp ; }
18471968 void setSP (uint64_t value) { _registers.__sp = value; }
1848- uint64_t getIP () const { return _registers.__pc ; }
1849- void setIP (uint64_t value) { _registers.__pc = value; }
1969+ uint64_t getIP () const { return authPC (&_registers, getDiscriminator ()); }
1970+ void setIP (uint64_t value) {
1971+ // First authenticate the current value of the IP to ensure the context
1972+ // is still valid. This also ensure the setIP can't be used for signing
1973+ // arbitrary values.
1974+ authPC (&_registers, getDiscriminator ());
1975+ updatePC (value);
1976+ }
18501977 uint64_t getFP () const { return _registers.__fp ; }
18511978 void setFP (uint64_t value) { _registers.__fp = value; }
18521979
@@ -1858,12 +1985,15 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
18581985 uint64_t __sp; // Stack pointer x31
18591986 uint64_t __pc; // Program counter
18601987 uint64_t __ra_sign_state; // RA sign state register
1988+ #if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
1989+ uint64_t __pc2; // PC's signed upper part
1990+ #endif
18611991 };
18621992
18631993 GPRs _registers;
18641994 double _vectorHalfRegisters[32 ];
1865- // Currently only the lower double in 128-bit vectore registers
1866- // is perserved during unwinding. We could define new register
1995+ // Currently only the lower double in 128-bit vector registers
1996+ // is preserved during unwinding. We could define new register
18671997 // numbers (> 96) which mean whole vector registers, then this
18681998 // struct would need to change to contain whole vector registers.
18691999};
@@ -1872,8 +2002,15 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
18722002 static_assert ((check_fit<Registers_arm64, unw_context_t >::does_fit),
18732003 " arm64 registers do not fit into unw_context_t" );
18742004 memcpy (&_registers, registers, sizeof (_registers));
2005+ #if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
2006+ static_assert (sizeof (GPRs) == 0x118 ,
2007+ " expected VFP registers to be at offset 280" );
2008+ #else
18752009 static_assert (sizeof (GPRs) == 0x110 ,
18762010 " expected VFP registers to be at offset 272" );
2011+ #endif
2012+ // getcontext signs the PC with the base address of the context.
2013+ resignPC (reinterpret_cast <uint64_t >(registers));
18772014 memcpy (_vectorHalfRegisters,
18782015 static_cast <const uint8_t *>(registers) + sizeof (GPRs),
18792016 sizeof (_vectorHalfRegisters));
@@ -1882,6 +2019,25 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
18822019inline Registers_arm64::Registers_arm64 () {
18832020 memset (&_registers, 0 , sizeof (_registers));
18842021 memset (&_vectorHalfRegisters, 0 , sizeof (_vectorHalfRegisters));
2022+ // We don't know the value of the PC but let's sign it to indicate we have a
2023+ // valid register set.
2024+ updatePC (0 );
2025+ }
2026+
2027+ inline Registers_arm64::Registers_arm64 (const Registers_arm64 &other) {
2028+ memcpy (&_registers, &other._registers , sizeof (_registers));
2029+ memcpy (&_vectorHalfRegisters, &other._vectorHalfRegisters ,
2030+ sizeof (_vectorHalfRegisters));
2031+ resignPC (other.getDiscriminator ());
2032+ }
2033+
2034+ inline Registers_arm64 &
2035+ Registers_arm64::operator =(const Registers_arm64 &other) {
2036+ memcpy (&_registers, &other._registers , sizeof (_registers));
2037+ memcpy (&_vectorHalfRegisters, &other._vectorHalfRegisters ,
2038+ sizeof (_vectorHalfRegisters));
2039+ resignPC (other.getDiscriminator ());
2040+ return *this ;
18852041}
18862042
18872043inline bool Registers_arm64::validRegister (int regNum) const {
@@ -1902,7 +2058,7 @@ inline bool Registers_arm64::validRegister(int regNum) const {
19022058
19032059inline uint64_t Registers_arm64::getRegister (int regNum) const {
19042060 if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
1905- return _registers. __pc ;
2061+ return getIP () ;
19062062 if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
19072063 return _registers.__sp ;
19082064 if (regNum == UNW_AARCH64_RA_SIGN_STATE)
@@ -1918,7 +2074,7 @@ inline uint64_t Registers_arm64::getRegister(int regNum) const {
19182074
19192075inline void Registers_arm64::setRegister (int regNum, uint64_t value) {
19202076 if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
1921- _registers. __pc = value;
2077+ setIP ( value) ;
19222078 else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
19232079 _registers.__sp = value;
19242080 else if (regNum == UNW_AARCH64_RA_SIGN_STATE)
0 commit comments