Skip to content

Commit b40594b

Browse files
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
1 parent 114455c commit b40594b

File tree

3 files changed

+54
-30
lines changed

3 files changed

+54
-30
lines changed

libunwind/src/Registers.hpp

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,19 +1829,18 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
18291829
struct GPRs;
18301830

18311831
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-
}
18421832
#if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
1833+
/// The program counter is effectively used as a return address when the
1834+
/// context is restored; therefore, it should be protected with PAC to prevent
1835+
/// arbitrary returns. The base address of the context is blended with a
1836+
/// discriminator using the A key for authentication and signing. Return
1837+
/// address authentication is still managed according to the unwind
1838+
/// information. In some cases, the LR contains significant bits in the space
1839+
/// reserved for PAC bits, so the value of the PC is stored in two halves.
1840+
/// The second half is signed using the first half as a discriminator to bind
1841+
/// the two halves together.
18431842
#if defined(_LIBUNWIND_PTRAUTH_AVAILABLE)
1844-
/// Use Pointer Authentication Intrinsics when available.
1843+
/// Use Pointer Authentication Intrinsics when available.
18451844
#define __libunwind_ptrauth_auth_data(__value, __key, __discriminator) \
18461845
ptrauth_auth_data(__value, __key, __discriminator)
18471846
#define __libunwind_ptrauth_auth_and_resign(pointer, oldKey, oldDiscriminator, \
@@ -1850,6 +1849,8 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
18501849
newDiscriminator)
18511850
#define __libunwind_ptrauth_sign_unauthenticated(__value, __key, __data) \
18521851
ptrauth_sign_unauthenticated(__value, __key, __data)
1852+
#define __libunwind_ptrauth_blend_discriminator(__ptr, __data) \
1853+
ptrauth_blend_discriminator(__ptr, __data)
18531854
#else // !_LIBUNWIND_PTRAUTH_AVAILABLE
18541855
typedef enum {
18551856
ptrauth_key_asia = 0,
@@ -1900,14 +1901,21 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
19001901
__libunwind_ptrauth_auth_data(pointer, oldKey, oldDiscriminator),
19011902
newKey, newDiscriminator);
19021903
}
1904+
#define __libunwind_ptrauth_blend_discriminator(__ptr, __data) \
1905+
ptrauth_blend_discriminator(__ptr, __data)
19031906
#endif
1907+
inline uint64_t
1908+
__libunwind_ptrauth_blend_discriminator(uint64_t __ptr,
1909+
uint16_t __data) const {
1910+
return (__ptr & (~0 >> 16)) | ((uint64_t)__data << 48);
1911+
}
19041912
// Authenticate the currently stored PC and return it's raw value.
19051913
inline uint64_t authPC(const struct GPRs *gprs,
19061914
uint64_t discriminator) const {
1915+
uint64_t upper = (uint64_t)__libunwind_ptrauth_auth_data(
1916+
(void *)gprs->__pc2, ptrauth_key_asia, gprs->__pc);
19071917
uint64_t lower = (uint64_t)__libunwind_ptrauth_auth_data(
19081918
(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);
19111919
return (upper << 32) | lower;
19121920
}
19131921

@@ -1917,19 +1925,26 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
19171925
(void *)(value & (((uint64_t)~0) >> 32)), ptrauth_key_asia,
19181926
getDiscriminator());
19191927
_registers.__pc2 = (uint64_t)__libunwind_ptrauth_sign_unauthenticated(
1920-
(void *)(value >> 32), ptrauth_key_asia, getDiscriminator());
1928+
(void *)(value >> 32), ptrauth_key_asia, _registers.__pc);
19211929
}
19221930

19231931
// Update the signature on the current PC.
19241932
inline void resignPC(uint64_t oldDiscriminator) {
1933+
uint64_t old_signed_pc = _registers.__pc;
19251934
_registers.__pc = (uint64_t)__libunwind_ptrauth_auth_and_resign(
19261935
(void *)_registers.__pc, ptrauth_key_asia, oldDiscriminator,
19271936
ptrauth_key_asia, getDiscriminator());
19281937
_registers.__pc2 = (uint64_t)__libunwind_ptrauth_auth_and_resign(
1929-
(void *)_registers.__pc2, ptrauth_key_asia, oldDiscriminator,
1930-
ptrauth_key_asia, getDiscriminator());
1938+
(void *)_registers.__pc2, ptrauth_key_asia, old_signed_pc,
1939+
ptrauth_key_asia, _registers.__pc);
19311940
}
19321941
#else //! defined(_LIBUNWIND_AARCH64_PC_PROTECTION))
1942+
inline uint64_t
1943+
__libunwind_ptrauth_blend_discriminator(uint64_t __ptr,
1944+
uint16_t __data) const {
1945+
(void)__data;
1946+
return __ptr;
1947+
}
19331948
// Remote unwinding is not supported by this protection.
19341949
inline uint64_t authPC(const struct GPRs *gprs,
19351950
const uint64_t discriminator) const {
@@ -1940,6 +1955,11 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
19401955
inline void resignPC(uint64_t oldDiscriminator) { (void)oldDiscriminator; }
19411956
#endif
19421957

1958+
inline uint64_t getDiscriminator() const {
1959+
return __libunwind_ptrauth_blend_discriminator(
1960+
reinterpret_cast<uint64_t>(this), 0xface);
1961+
}
1962+
19431963
public:
19441964
Registers_arm64();
19451965
Registers_arm64(const void *registers);
@@ -2010,7 +2030,8 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
20102030
"expected VFP registers to be at offset 272");
20112031
#endif
20122032
// getcontext signs the PC with the base address of the context.
2013-
resignPC(reinterpret_cast<uint64_t>(registers));
2033+
resignPC(__libunwind_ptrauth_blend_discriminator(
2034+
reinterpret_cast<uint64_t>(registers), 0xface));
20142035
memcpy(_vectorHalfRegisters,
20152036
static_cast<const uint8_t *>(registers) + sizeof(GPRs),
20162037
sizeof(_vectorHalfRegisters));

libunwind/src/UnwindRegistersRestore.S

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -659,24 +659,26 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
659659
ldp x28,x29, [x0, #0x0E0]
660660
#if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
661661
#define __VOFFSET 0x118
662-
// Authenticate return address with the address of the context.
663-
ldr x30, [x0, #0x100] // __pc
662+
// See the description in Registers_arm64
663+
ldr x30, [x0, #0x100] // __pc
664664
mov x17, x30
665-
mov x16, x0
666-
hint 0xc // autia1716
667-
xpaclri
668-
cmp x17, x30
665+
mov x16, x0 // auth with the buffer's address
666+
movk x16, #0xface, lsl #48 // blinded with a constant
667+
hint 0xc // autia1716
668+
hint 0x7 // xpaclri
669+
cmp x17, x30 // check if the auth failed
669670
b.ne LauthError
670671
mov x1, x17
671-
ldr x30, [x0, #0x110] // __pc2
672+
ldr x30, [x0, #0x110] // __pc2
672673
mov x17, x30
673-
hint 0xc // autia1716
674-
xpaclri
675-
cmp x17, x30
674+
ldr x16, [x0, #0x100] // Auth the second part with the signed first part
675+
hint 0xc // autia1716
676+
hint 0x7 // xpaclri
677+
cmp x17, x30 // check if the auth failed
676678
b.ne LauthError
677679
lsl x17, x17, 32
678680
orr x30, x17, x1
679-
mov x16, xzr
681+
mov x16, xzr // clearup registers
680682
mov x17, xzr
681683
#else
682684
#define __VOFFSET 0x110

libunwind/src/UnwindRegistersSave.S

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -746,11 +746,12 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
746746
str x1, [x0, #0x0F8]
747747
#if defined(_LIBUNWIND_AARCH64_PC_PROTECTION)
748748
#define __VOFFSET 0x118
749-
// Sign the return address as pc with the address of the context
750-
// buttom and the top word signed and stored sepearetly.
749+
// See the description in Registers_arm64
751750
mov w17, w30 // lower part of the PC
752751
mov x16, x0 // sign with the address of the context
752+
movk x16, #0xface, lsl #48 // blinded with a constant
753753
hint 0x8 // pacia1716
754+
mov x16, x17 // sign the second part with the first part
754755
str x17, [x0, #0x100] // store to __pc
755756
mov x17, x30 // upper part of the PC
756757
lsr x17, x17, 32

0 commit comments

Comments
 (0)