From ef8f52e5bec31b8fb0e96312b2bcdabc31a5c10c Mon Sep 17 00:00:00 2001 From: Jack Styles Date: Thu, 3 Oct 2024 14:20:10 +0100 Subject: [PATCH] [PAuthLR] Add support for FEAT_PAuth_LR to libunwind This introduces support for unwinding programs where return addresses have been signed using FEAT_PAuth_Lr, where the value of PC is used as a diversifier (-mbranch-protection=pac-ret+pc). A new vendor specific call frame instruction is added, named `DW_CFA_AARCH64_negate_ra_state_with_pc`, to instruct the unwinder tocapture the value of PC at the point of signing and update bit 1 of the existing `RA_SIGN_STATE` pseudo-register to flag the need to use it for authentication. See https://github.com/ARM-software/abi-aa/pull/245 for the ABI change. Authored-by: pratlucas --- libunwind/src/DwarfInstructions.hpp | 54 ++++++++++++++++++++++------- libunwind/src/DwarfParser.hpp | 20 +++++++++++ libunwind/src/dwarf2.h | 3 +- 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index bd9ece60ee5881a..e7c467de80adb68 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -74,8 +74,10 @@ class DwarfInstructions { __builtin_unreachable(); } #if defined(_LIBUNWIND_TARGET_AARCH64) - static bool getRA_SIGN_STATE(A &addressSpace, R registers, pint_t cfa, - PrologInfo &prolog); + static bool isReturnAddressSigned(A &addressSpace, R registers, pint_t cfa, + PrologInfo &prolog); + static bool isReturnAddressSignedWithPC(A &addressSpace, R registers, + pint_t cfa, PrologInfo &prolog); #endif }; @@ -173,8 +175,9 @@ v128 DwarfInstructions::getSavedVectorRegister( } #if defined(_LIBUNWIND_TARGET_AARCH64) template -bool DwarfInstructions::getRA_SIGN_STATE(A &addressSpace, R registers, - pint_t cfa, PrologInfo &prolog) { +bool DwarfInstructions::isReturnAddressSigned(A &addressSpace, + R registers, pint_t cfa, + PrologInfo &prolog) { pint_t raSignState; auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE]; if (regloc.location == CFI_Parser::kRegisterUnused) @@ -185,6 +188,22 @@ bool DwarfInstructions::getRA_SIGN_STATE(A &addressSpace, R registers, // Only bit[0] is meaningful. return raSignState & 0x01; } + +template +bool DwarfInstructions::isReturnAddressSignedWithPC(A &addressSpace, + R registers, + pint_t cfa, + PrologInfo &prolog) { + pint_t raSignState; + auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE]; + if (regloc.location == CFI_Parser::kRegisterUnused) + raSignState = static_cast(regloc.value); + else + raSignState = getSavedRegister(addressSpace, registers, cfa, regloc); + + // Only bit[1] is meaningful. + return raSignState & 0x02; +} #endif template @@ -288,7 +307,7 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, // restored. autia1716 is used instead of autia as autia1716 assembles // to a NOP on pre-v8.3a architectures. if ((R::getArch() == REGISTERS_ARM64) && - getRA_SIGN_STATE(addressSpace, registers, cfa, prolog) && + isReturnAddressSigned(addressSpace, registers, cfa, prolog) && returnAddress != 0) { #if !defined(_LIBUNWIND_IS_NATIVE_ONLY) return UNW_ECROSSRASIGNING; @@ -296,13 +315,24 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, register unsigned long long x17 __asm("x17") = returnAddress; register unsigned long long x16 __asm("x16") = cfa; - // These are the autia1716/autib1716 instructions. The hint instructions - // are used here as gcc does not assemble autia1716/autib1716 for pre - // armv8.3a targets. - if (cieInfo.addressesSignedWithBKey) - asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716 - else - asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716 + // 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 } diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp index 0682942ce13799e..b104d773ed44407 100644 --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -91,6 +91,9 @@ class CFI_Parser { int64_t cfaExpression; // CFA = expression uint32_t spExtraArgSize; RegisterLocation savedRegisters[kMaxRegisterNumber + 1]; + #if defined(_LIBUNWIND_TARGET_AARCH64) + pint_t ptrAuthDiversifier; + #endif enum class InitializeTime { kLazy, kNormal }; // When saving registers, this data structure is lazily initialized. @@ -799,6 +802,23 @@ bool CFI_Parser::parseFDEInstructions(A &addressSpace, } break; +#if defined(_LIBUNWIND_TARGET_AARCH64) + case DW_CFA_AARCH64_negate_ra_state_with_pc: { + int64_t value = + results->savedRegisters[UNW_AARCH64_RA_SIGN_STATE].value ^ 0x3; + results->setRegisterValue(UNW_AARCH64_RA_SIGN_STATE, value, + initialState); + // When calucating the value of the PC, it is assumed that the CFI instruction + // is placed before the signing instruction, however it is placed after. Because + // of this, we need to take into account the CFI instruction is one instruction + // call later than expected, and reduce the PC value by 4 bytes to compensate. + results->ptrAuthDiversifier = fdeInfo.pcStart + codeOffset - 0x4; + _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state_with_pc(pc=0x%" PRIx64 ")\n", + static_cast(results->ptrAuthDiversifier)); + } + break; +#endif + #else (void)arch; #endif diff --git a/libunwind/src/dwarf2.h b/libunwind/src/dwarf2.h index 174277d5a795084..2ad3d3c464e80da 100644 --- a/libunwind/src/dwarf2.h +++ b/libunwind/src/dwarf2.h @@ -51,7 +51,8 @@ enum { DW_CFA_GNU_negative_offset_extended = 0x2F, // AARCH64 extensions - DW_CFA_AARCH64_negate_ra_state = 0x2D + DW_CFA_AARCH64_negate_ra_state_with_pc = 0x2C, + DW_CFA_AARCH64_negate_ra_state = 0x2D };