-
Notifications
You must be signed in to change notification settings - Fork 15.2k
Description
#143230 introduces libunwind hardening which could be used with pauth-enabled ABIs (such as arm64e for Apple and pauthtest for Linux). This includes handling of signed return addresses. Both mentioned ABIs implicitly enable -fptrauth-returns
flag, which sets __has_feature(ptrauth_returns)
to true and triggers RA signing in codegen via setting "ptrauth-returns"
attribute for functions in IR.
The existing logic in libunwind already handles signed RA - such handling was introduced previously to support signed RA appearing when -mbranch-protection=pac-ret
is used:
llvm-project/libunwind/src/DwarfInstructions.hpp
Lines 303 to 344 in 3cb2174
#if defined(_LIBUNWIND_TARGET_AARCH64) | |
// If the target is aarch64 then the return address may have been signed | |
// using the v8.3 pointer authentication extensions. The original | |
// return address needs to be authenticated before the return address is | |
// restored. autia1716 is used instead of autia as autia1716 assembles | |
// to a NOP on pre-v8.3a architectures. | |
if ((R::getArch() == REGISTERS_ARM64) && | |
isReturnAddressSigned(addressSpace, registers, cfa, prolog) && | |
returnAddress != 0) { | |
#if !defined(_LIBUNWIND_IS_NATIVE_ONLY) | |
return UNW_ECROSSRASIGNING; | |
#else | |
register unsigned long long x17 __asm("x17") = returnAddress; | |
register unsigned long long x16 __asm("x16") = cfa; | |
// We use the hint versions of the authentication instructions below to | |
// ensure they're assembled by the compiler even for targets with no | |
// FEAT_PAuth/FEAT_PAuth_LR support. | |
if (isReturnAddressSignedWithPC(addressSpace, registers, cfa, prolog)) { | |
register unsigned long long x15 __asm("x15") = | |
prolog.ptrAuthDiversifier; | |
if (cieInfo.addressesSignedWithBKey) { | |
asm("hint 0x27\n\t" // pacm | |
"hint 0xe" | |
: "+r"(x17) | |
: "r"(x16), "r"(x15)); // autib1716 | |
} else { | |
asm("hint 0x27\n\t" // pacm | |
"hint 0xc" | |
: "+r"(x17) | |
: "r"(x16), "r"(x15)); // autia1716 | |
} | |
} else { | |
if (cieInfo.addressesSignedWithBKey) | |
asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716 | |
else | |
asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716 | |
} | |
returnAddress = x17; | |
#endif | |
} | |
#endif |
Note that existing implementation in libunwind supports different RA sign state in different stack frames. For example, we can build libunwind itself w/o pac-ret enabled, and build user code with pac-ret enabled, and unwinding would work fine since we look at RA sign state in each individual frame.
After #143230, signed RA handling looks as follows:
- If libunwind is built with pauth-enabled ABIs such as arm64e and pauthtest (which imply
__has_feature(ptrauth_calls) && __has_feature(ptrauth_returns)
, we skip old signed RA handling logic and use signed RA handling logic from [runtimes][PAC] Harden unwinding when possible #143230. - Otherwise, we use old signed RA handling logic.
Actual codegen is the same for -fptrauth-returns
and -mbranch-protection=pac-ret
, we just have different ways enabling that (see also #143230 (comment)).
We should unify logic in libunwind for these two cases as well.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status