19
19
#include " libunwind.h"
20
20
#include " shadow_stack_unwind.h"
21
21
22
+ #if defined(_LIBUNWIND_PTRAUTH_AVAILABLE)
23
+ #include < ptrauth.h>
24
+ #endif
22
25
namespace libunwind {
23
26
24
27
// For emulating 128-bit registers
@@ -1823,9 +1826,127 @@ extern "C" void *__libunwind_shstk_get_jump_target() {
1823
1826
#endif
1824
1827
1825
1828
class _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
+
1826
1943
public:
1827
1944
Registers_arm64 ();
1828
1945
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 ;
1829
1950
1830
1951
bool validRegister (int num) const ;
1831
1952
uint64_t getRegister (int num) const ;
@@ -1845,8 +1966,14 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
1845
1966
1846
1967
uint64_t getSP () const { return _registers.__sp ; }
1847
1968
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
+ }
1850
1977
uint64_t getFP () const { return _registers.__fp ; }
1851
1978
void setFP (uint64_t value) { _registers.__fp = value; }
1852
1979
@@ -1858,12 +1985,15 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
1858
1985
uint64_t __sp; // Stack pointer x31
1859
1986
uint64_t __pc; // Program counter
1860
1987
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
1861
1991
};
1862
1992
1863
1993
GPRs _registers;
1864
1994
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
1867
1997
// numbers (> 96) which mean whole vector registers, then this
1868
1998
// struct would need to change to contain whole vector registers.
1869
1999
};
@@ -1872,8 +2002,15 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
1872
2002
static_assert ((check_fit<Registers_arm64, unw_context_t >::does_fit),
1873
2003
" arm64 registers do not fit into unw_context_t" );
1874
2004
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
1875
2009
static_assert (sizeof (GPRs) == 0x110 ,
1876
2010
" 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));
1877
2014
memcpy (_vectorHalfRegisters,
1878
2015
static_cast <const uint8_t *>(registers) + sizeof (GPRs),
1879
2016
sizeof (_vectorHalfRegisters));
@@ -1882,6 +2019,25 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
1882
2019
inline Registers_arm64::Registers_arm64 () {
1883
2020
memset (&_registers, 0 , sizeof (_registers));
1884
2021
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 ;
1885
2041
}
1886
2042
1887
2043
inline bool Registers_arm64::validRegister (int regNum) const {
@@ -1902,7 +2058,7 @@ inline bool Registers_arm64::validRegister(int regNum) const {
1902
2058
1903
2059
inline uint64_t Registers_arm64::getRegister (int regNum) const {
1904
2060
if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
1905
- return _registers. __pc ;
2061
+ return getIP () ;
1906
2062
if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
1907
2063
return _registers.__sp ;
1908
2064
if (regNum == UNW_AARCH64_RA_SIGN_STATE)
@@ -1918,7 +2074,7 @@ inline uint64_t Registers_arm64::getRegister(int regNum) const {
1918
2074
1919
2075
inline void Registers_arm64::setRegister (int regNum, uint64_t value) {
1920
2076
if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC)
1921
- _registers. __pc = value;
2077
+ setIP ( value) ;
1922
2078
else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP)
1923
2079
_registers.__sp = value;
1924
2080
else if (regNum == UNW_AARCH64_RA_SIGN_STATE)
0 commit comments