Skip to content

Commit ef7d080

Browse files
committed
[PAC][libunwind][AArch64] Keep LR signed when stored in context struct
There are two ways of return address signing: pac-ret (enabled via `-mbranch-protection=pac-ret`) and ptrauth-returns (enabled as part of Apple's arm64e or experimental pauthtest ABI on Linux). Previously, signed LR was handled in libunwind as follows: 1. For pac-ret, the signed LR value was authenticated, and the resulting unsigned LR value was stored in the context structure. 2. For ptrauth-returns (which is assumed to be a part of a full-fledged PAuth ABI like arm64e or pauthtest), the signed LR value was re-signed using the same key (IB) and address of the `__pc` field in the context structure as a modifier. This patch unifies the signed LR handling logic by keeping LR signed for pac-ret similarly to ptrauth-returns. It makes LR substitution in the context structure harder. Note that LR signed state or signing scheme for pac-ret might differ between stack frames. The behavior differs from ptrauth-returns, which has a fixed signing scheme and is enabled as a part of bigger PAuth ABI which is either present everywhere or not. In order to handle different signing schemes across stack frames, new subfields `__state`, `__second_modifier` and `__use_b_key` of the field `__ra_sign` in the context structure are used. When stored in the context structure, the pointer is resigned with maintaining original key and using the address of the `__pc` field in the structure as a modifier.
1 parent d714a6c commit ef7d080

File tree

9 files changed

+482
-171
lines changed

9 files changed

+482
-171
lines changed

libunwind/include/__libunwind_config.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,11 @@
7373
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC
7474
# elif defined(__aarch64__)
7575
# define _LIBUNWIND_TARGET_AARCH64 1
76-
#define _LIBUNWIND_CONTEXT_SIZE 67
76+
#define _LIBUNWIND_CONTEXT_SIZE 69
7777
# if defined(__SEH__)
7878
# define _LIBUNWIND_CURSOR_SIZE 164
7979
# else
80-
#define _LIBUNWIND_CURSOR_SIZE 79
80+
#define _LIBUNWIND_CURSOR_SIZE 81
8181
# endif
8282
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64
8383
# elif defined(__arm__)

libunwind/include/libunwind.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,12 @@ enum {
642642
// reserved block
643643
UNW_AARCH64_RA_SIGN_STATE = 34,
644644

645+
// The following two registers are not real Dwarf registers and use numbers
646+
// which are not occupied by real Dwarf registers according to
647+
// https://github.com/ARM-software/abi-aa/blob/2025Q1/aadwarf64/aadwarf64.rst
648+
UNW_AARCH64_RA_SIGN_SECOND_MODIFIER = 128,
649+
UNW_AARCH64_RA_SIGN_USE_B_KEY = 129,
650+
645651
// FP/vector registers
646652
UNW_AARCH64_V0 = 64,
647653
UNW_AARCH64_V1 = 65,

libunwind/src/DwarfInstructions.hpp

Lines changed: 20 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,8 @@ class DwarfInstructions {
7575
__builtin_unreachable();
7676
}
7777
#if defined(_LIBUNWIND_TARGET_AARCH64)
78-
static bool isReturnAddressSigned(A &addressSpace, R registers, pint_t cfa,
79-
PrologInfo &prolog);
80-
static bool isReturnAddressSignedWithPC(A &addressSpace, R registers,
81-
pint_t cfa, PrologInfo &prolog);
78+
static pint_t getRASignState(A &addressSpace, const R &registers, pint_t cfa,
79+
const PrologInfo &prolog);
8280
#endif
8381
};
8482

@@ -176,34 +174,13 @@ v128 DwarfInstructions<A, R>::getSavedVectorRegister(
176174
}
177175
#if defined(_LIBUNWIND_TARGET_AARCH64)
178176
template <typename A, typename R>
179-
bool DwarfInstructions<A, R>::isReturnAddressSigned(A &addressSpace,
180-
R registers, pint_t cfa,
181-
PrologInfo &prolog) {
182-
pint_t raSignState;
183-
auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE];
184-
if (regloc.location == CFI_Parser<A>::kRegisterUnused)
185-
raSignState = static_cast<pint_t>(regloc.value);
186-
else
187-
raSignState = getSavedRegister(addressSpace, registers, cfa, regloc);
188-
189-
// Only bit[0] is meaningful.
190-
return raSignState & 0x01;
191-
}
192-
193-
template <typename A, typename R>
194-
bool DwarfInstructions<A, R>::isReturnAddressSignedWithPC(A &addressSpace,
195-
R registers,
196-
pint_t cfa,
197-
PrologInfo &prolog) {
198-
pint_t raSignState;
177+
typename A::pint_t
178+
DwarfInstructions<A, R>::getRASignState(A &addressSpace, const R &registers,
179+
pint_t cfa, const PrologInfo &prolog) {
199180
auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE];
200181
if (regloc.location == CFI_Parser<A>::kRegisterUnused)
201-
raSignState = static_cast<pint_t>(regloc.value);
202-
else
203-
raSignState = getSavedRegister(addressSpace, registers, cfa, regloc);
204-
205-
// Only bit[1] is meaningful.
206-
return raSignState & 0x02;
182+
return static_cast<pint_t>(regloc.value);
183+
return getSavedRegister(addressSpace, registers, cfa, regloc);
207184
}
208185
#endif
209186

@@ -302,54 +279,25 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace,
302279

303280
isSignalFrame = cieInfo.isSignalFrame;
304281

305-
#if defined(_LIBUNWIND_TARGET_AARCH64) && \
306-
!defined(_LIBUNWIND_TARGET_AARCH64_AUTHENTICATED_UNWINDING)
307-
// There are two ways of return address signing: pac-ret (enabled via
308-
// -mbranch-protection=pac-ret) and ptrauth-returns (enabled as part of
309-
// Apple's arm64e or experimental pauthtest ABI on Linux). The code
310-
// below handles signed RA for pac-ret, while ptrauth-returns uses
311-
// different logic.
312-
// TODO: unify logic for both cases, see
313-
// https://github.com/llvm/llvm-project/issues/160110
314-
//
282+
#if defined(_LIBUNWIND_TARGET_AARCH64)
315283
// If the target is aarch64 then the return address may have been signed
316-
// using the v8.3 pointer authentication extensions. The original
317-
// return address needs to be authenticated before the return address is
318-
// restored. autia1716 is used instead of autia as autia1716 assembles
319-
// to a NOP on pre-v8.3a architectures.
320-
if ((R::getArch() == REGISTERS_ARM64) &&
321-
isReturnAddressSigned(addressSpace, registers, cfa, prolog) &&
284+
// using the v8.3 pointer authentication extensions. In order to
285+
// store signed return address in the registers context structure, we
286+
// need to save the signing scheme for this address.
287+
pint_t raSignState = getRASignState(addressSpace, registers, cfa, prolog);
288+
bool isReturnAddressSigned = (raSignState & 1);
289+
if ((R::getArch() == REGISTERS_ARM64) && isReturnAddressSigned &&
322290
returnAddress != 0) {
323291
#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
324292
return UNW_ECROSSRASIGNING;
325293
#else
326-
register unsigned long long x17 __asm("x17") = returnAddress;
327-
register unsigned long long x16 __asm("x16") = cfa;
328-
329-
// We use the hint versions of the authentication instructions below to
330-
// ensure they're assembled by the compiler even for targets with no
331-
// FEAT_PAuth/FEAT_PAuth_LR support.
332-
if (isReturnAddressSignedWithPC(addressSpace, registers, cfa, prolog)) {
333-
register unsigned long long x15 __asm("x15") =
334-
prolog.ptrAuthDiversifier;
335-
if (cieInfo.addressesSignedWithBKey) {
336-
asm("hint 0x27\n\t" // pacm
337-
"hint 0xe"
338-
: "+r"(x17)
339-
: "r"(x16), "r"(x15)); // autib1716
340-
} else {
341-
asm("hint 0x27\n\t" // pacm
342-
"hint 0xc"
343-
: "+r"(x17)
344-
: "r"(x16), "r"(x15)); // autia1716
345-
}
346-
} else {
347-
if (cieInfo.addressesSignedWithBKey)
348-
asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
349-
else
350-
asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
294+
newRegisters.setRegister(UNW_AARCH64_RA_SIGN_STATE, raSignState);
295+
if (newRegisters.isReturnAddressSignedWithPC()) {
296+
newRegisters.setRegister(UNW_AARCH64_RA_SIGN_SECOND_MODIFIER,
297+
prolog.ptrAuthDiversifier);
351298
}
352-
returnAddress = x17;
299+
newRegisters.setRegister(UNW_AARCH64_RA_SIGN_USE_B_KEY,
300+
cieInfo.addressesSignedWithBKey ? 1 : 0);
353301
#endif
354302
}
355303
#endif

0 commit comments

Comments
 (0)