From 2b25e92948403c15d1adf07daa09defcd0b38caf Mon Sep 17 00:00:00 2001 From: Daniil Kovalev Date: Fri, 2 Jan 2026 15:28:30 +0300 Subject: [PATCH] [PAC][libunwind] Pass ptrauth-qualified values as const references (#173765) For Apple's arm64e or Linux's pauthtest, `Registers_arm64::link_reg_t` type is `__ptrauth`-qualified. When passing a value of such a type to a function accepting non-`__ptrauth`-qualified parameter with `pint_t` type, an authentication is performed. So, the corresponding callee argument does not contain an embedded signature, making it prone to substitution if spilled to the stack. This patch prevents early authentication of signed values of `link_reg_t` type by passing them as const l-value references instead of passing by value with type `pint_t`. This way, the callee would operate with a `__ptrauth`-qualified value containing a signature, allowing to detect a substitution if the value is spilled to the stack. Note that this approach was introduced previously in #143230 for some other functions. In this patch, we apply the approach to the functions which were not considered previously. --- libunwind/src/AddressSpace.hpp | 32 ++++++++++----- libunwind/src/DwarfInstructions.hpp | 14 +++---- libunwind/src/DwarfParser.hpp | 28 ++++++++----- libunwind/src/EHHeaderParser.hpp | 11 +++-- libunwind/src/Registers.hpp | 23 +++++++++++ libunwind/src/UnwindCursor.hpp | 63 +++++++++++++++-------------- 6 files changed, 108 insertions(+), 63 deletions(-) diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp index 63f9cb367ec0..52477b16b355 100644 --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -202,10 +202,15 @@ public: pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, pint_t datarelBase = 0, pint_t *resultAddr = nullptr); - bool findFunctionName(pint_t addr, char *buf, size_t bufLen, - unw_word_t *offset); - bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info); - bool findOtherFDE(pint_t targetAddr, pint_t &fde); + template + bool findFunctionName(typename R::link_hardened_reg_arg_t addr, char *buf, + size_t bufLen, unw_word_t *offset); + template + bool findUnwindSections(typename R::link_hardened_reg_arg_t targetAddr, + UnwindInfoSections &info); + template + bool findOtherFDE(typename R::link_hardened_reg_arg_t targetAddr, + pint_t &fde); static LocalAddressSpace sThisAddressSpace; }; @@ -497,9 +502,9 @@ static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo, #endif // defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) - -inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, - UnwindInfoSections &info) { +template +inline bool LocalAddressSpace::findUnwindSections( + typename R::link_hardened_reg_arg_t targetAddr, UnwindInfoSections &info) { #ifdef __APPLE__ dyld_unwind_sections dyldInfo; if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) { @@ -669,16 +674,21 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, return false; } -inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) { +template +inline bool +LocalAddressSpace::findOtherFDE(typename R::link_hardened_reg_arg_t targetAddr, + pint_t &fde) { // TO DO: if OS has way to dynamically register FDEs, check that. (void)targetAddr; (void)fde; return false; } -inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf, - size_t bufLen, - unw_word_t *offset) { +template +inline bool +LocalAddressSpace::findFunctionName(typename R::link_hardened_reg_arg_t addr, + char *buf, size_t bufLen, + unw_word_t *offset) { #if _LIBUNWIND_USE_DLADDR Dl_info dyldInfo; if (dladdr((void *)addr, &dyldInfo)) { diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index d2822e8be29e..165c4a99e9a9 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -33,7 +33,8 @@ public: typedef typename A::pint_t pint_t; typedef typename A::sint_t sint_t; - static int stepWithDwarf(A &addressSpace, const typename R::link_reg_t &pc, + static int stepWithDwarf(A &addressSpace, + typename R::link_hardened_reg_arg_t pc, pint_t fdeStart, R ®isters, bool &isSignalFrame, bool stage2); @@ -208,17 +209,16 @@ bool DwarfInstructions::isReturnAddressSignedWithPC(A &addressSpace, #endif template -int DwarfInstructions::stepWithDwarf(A &addressSpace, - const typename R::link_reg_t &pc, - pint_t fdeStart, R ®isters, - bool &isSignalFrame, bool stage2) { +int DwarfInstructions::stepWithDwarf( + A &addressSpace, typename R::link_hardened_reg_arg_t pc, pint_t fdeStart, + R ®isters, bool &isSignalFrame, bool stage2) { FDE_Info fdeInfo; CIE_Info cieInfo; if (CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, &cieInfo) == NULL) { PrologInfo prolog; - if (CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, - R::getArch(), &prolog)) { + if (CFI_Parser::template parseFDEInstructions( + addressSpace, fdeInfo, cieInfo, pc, R::getArch(), &prolog)) { // get pointer to cfa (architecture specific) pint_t cfa = getCFA(addressSpace, prolog, registers); diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp index 2b04ae2831f9..22de49023cb4 100644 --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -160,14 +160,17 @@ public: } }; - static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, - size_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo, - CIE_Info *cieInfo); + template + static bool findFDE(A &addressSpace, typename R::link_hardened_reg_arg_t pc, + pint_t ehSectionStart, size_t sectionLength, + pint_t fdeHint, FDE_Info *fdeInfo, CIE_Info *cieInfo); static const char *decodeFDE(A &addressSpace, pint_t fdeStart, FDE_Info *fdeInfo, CIE_Info *cieInfo, bool useCIEInfo = false); + template static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo, - const CIE_Info &cieInfo, pint_t upToPC, + const CIE_Info &cieInfo, + typename R::link_hardened_reg_arg_t upToPC, int arch, PrologInfo *results); static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo); @@ -239,9 +242,12 @@ const char *CFI_Parser::decodeFDE(A &addressSpace, pint_t fdeStart, /// Scan an eh_frame section to find an FDE for a pc template -bool CFI_Parser::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, - size_t sectionLength, pint_t fdeHint, - FDE_Info *fdeInfo, CIE_Info *cieInfo) { +template +bool CFI_Parser::findFDE(A &addressSpace, + typename R::link_hardened_reg_arg_t pc, + pint_t ehSectionStart, size_t sectionLength, + pint_t fdeHint, FDE_Info *fdeInfo, + CIE_Info *cieInfo) { //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; const pint_t ehSectionEnd = (sectionLength == SIZE_MAX) @@ -451,10 +457,10 @@ const char *CFI_Parser::parseCIE(A &addressSpace, pint_t cie, /// "run" the DWARF instructions and create the abstract PrologInfo for an FDE template -bool CFI_Parser::parseFDEInstructions(A &addressSpace, - const FDE_Info &fdeInfo, - const CIE_Info &cieInfo, pint_t upToPC, - int arch, PrologInfo *results) { +template +bool CFI_Parser::parseFDEInstructions( + A &addressSpace, const FDE_Info &fdeInfo, const CIE_Info &cieInfo, + typename R::link_hardened_reg_arg_t upToPC, int arch, PrologInfo *results) { // Alloca is used for the allocation of the rememberStack entries. It removes // the dependency on new/malloc but the below for loop can not be refactored // into functions. Entry could be saved during the processing of a CIE and diff --git a/libunwind/src/EHHeaderParser.hpp b/libunwind/src/EHHeaderParser.hpp index 0662a1321e2c..b5d927027f64 100644 --- a/libunwind/src/EHHeaderParser.hpp +++ b/libunwind/src/EHHeaderParser.hpp @@ -37,8 +37,9 @@ public: static bool decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo); - static bool findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart, - uint32_t sectionLength, + template + static bool findFDE(A &addressSpace, typename R::link_hardened_reg_arg_t pc, + pint_t ehHdrStart, uint32_t sectionLength, typename CFI_Parser::FDE_Info *fdeInfo, typename CFI_Parser::CIE_Info *cieInfo); @@ -112,8 +113,10 @@ bool EHHeaderParser::decodeTableEntry( } template -bool EHHeaderParser::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart, - uint32_t sectionLength, +template +bool EHHeaderParser::findFDE(A &addressSpace, + typename R::link_hardened_reg_arg_t pc, + pint_t ehHdrStart, uint32_t sectionLength, typename CFI_Parser::FDE_Info *fdeInfo, typename CFI_Parser::CIE_Info *cieInfo) { pint_t ehHdrEnd = ehHdrStart + sectionLength; diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp index 45a2b0921ea3..474b17461bf7 100644 --- a/libunwind/src/Registers.hpp +++ b/libunwind/src/Registers.hpp @@ -68,6 +68,7 @@ public: typedef uint32_t reg_t; typedef uint32_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint32_t getRegister(int num) const; @@ -289,6 +290,7 @@ public: typedef uint64_t reg_t; typedef uint64_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint64_t getRegister(int num) const; @@ -611,6 +613,7 @@ public: typedef uint32_t reg_t; typedef uint32_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint32_t getRegister(int num) const; @@ -1186,6 +1189,7 @@ public: typedef uint64_t reg_t; typedef uint64_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint64_t getRegister(int num) const; @@ -1852,6 +1856,14 @@ public: typedef uint64_t reg_t; typedef uint64_t __ptrauth_unwind_registers_arm64_link_reg link_reg_t; + // Use `link_hardened_reg_arg_t` to pass values of `link_reg_t` type as + // function arguments. We need to use a const l-value reference to keep + // signature of `__ptrauth`-qualified values of `link_reg_t` type on AArch64 + // PAuth-enabled ABI intact. Passing the raw pointer by value would cause + // authentication on the caller side and make the pointer prone to + // substitution if spilled to the stack in the callee. + typedef const link_reg_t &link_hardened_reg_arg_t; + bool validRegister(int num) const; uint64_t getRegister(int num) const; void setRegister(int num, uint64_t value); @@ -2257,6 +2269,7 @@ public: typedef uint32_t reg_t; typedef uint32_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint32_t getRegister(int num) const; @@ -2765,6 +2778,7 @@ public: typedef uint32_t reg_t; typedef uint32_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint32_t getRegister(int num) const; @@ -2967,6 +2981,7 @@ public: typedef uint32_t reg_t; typedef uint32_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint32_t getRegister(int num) const; @@ -3305,6 +3320,7 @@ public: typedef uint64_t reg_t; typedef uint64_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint64_t getRegister(int num) const; @@ -3611,6 +3627,7 @@ public: typedef uint32_t reg_t; typedef uint32_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint32_t getRegister(int num) const; @@ -3800,6 +3817,7 @@ public: typedef uint64_t reg_t; typedef uint64_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint64_t getRegister(int num) const; @@ -3988,6 +4006,7 @@ public: typedef uint32_t reg_t; typedef uint32_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint32_t getRegister(int num) const; @@ -4206,6 +4225,7 @@ public: typedef ::libunwind::reg_t reg_t; typedef ::libunwind::reg_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; reg_t getRegister(int num) const; @@ -4506,6 +4526,7 @@ public: typedef uint64_t reg_t; typedef uint64_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint64_t getRegister(int num) const; @@ -4952,6 +4973,7 @@ public: typedef uint64_t reg_t; typedef uint64_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint64_t getRegister(int num) const; @@ -5243,6 +5265,7 @@ public: typedef uint64_t reg_t; typedef uint64_t link_reg_t; + typedef const link_reg_t &link_hardened_reg_arg_t; bool validRegister(int num) const; uint64_t getRegister(int num) const; diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index afa0cae79037..5838dbcaa998 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -121,7 +121,9 @@ class _LIBUNWIND_HIDDEN DwarfFDECache { typedef typename A::pint_t pint_t; public: static constexpr pint_t kSearchAll = static_cast(-1); - static pint_t findFDE(pint_t mh, pint_t pc); + template + static pint_t findFDE(pint_t mh, typename R::link_hardened_reg_arg_t pc); + static void add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde); static void removeAllIn(pint_t mh); static void iterateCacheEntries(void (*func)(unw_word_t ip_start, @@ -174,8 +176,9 @@ bool DwarfFDECache::_registeredForDyldUnloads = false; #endif template -typename DwarfFDECache::pint_t DwarfFDECache::findFDE(pint_t mh, - pint_t pc) { +template +typename DwarfFDECache::pint_t +DwarfFDECache::findFDE(pint_t mh, typename R::link_hardened_reg_arg_t pc) { pint_t result = 0; _LIBUNWIND_LOG_IF_FALSE(_lock.lock_shared()); for (entry *p = _buffer; p < _bufferUsed; ++p) { @@ -1060,8 +1063,9 @@ private: #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) bool getInfoFromFdeCie(const typename CFI_Parser::FDE_Info &fdeInfo, const typename CFI_Parser::CIE_Info &cieInfo, - pint_t pc, uintptr_t dso_base); - bool getInfoFromDwarfSection(const typename R::link_reg_t &pc, + typename R::link_hardened_reg_arg_t pc, + uintptr_t dso_base); + bool getInfoFromDwarfSection(typename R::link_hardened_reg_arg_t pc, const UnwindInfoSections §s, uint32_t fdeSectionOffsetHint = 0); int stepWithDwarfFDE(bool stage2) { @@ -1079,7 +1083,7 @@ private: #endif #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) - bool getInfoFromCompactEncodingSection(const typename R::link_reg_t &pc, + bool getInfoFromCompactEncodingSection(typename R::link_hardened_reg_arg_t pc, const UnwindInfoSections §s); int stepWithCompactEncoding(bool stage2 = false) { #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) @@ -1730,11 +1734,11 @@ bool UnwindCursor::getInfoFromEHABISection( template bool UnwindCursor::getInfoFromFdeCie( const typename CFI_Parser::FDE_Info &fdeInfo, - const typename CFI_Parser::CIE_Info &cieInfo, pint_t pc, - uintptr_t dso_base) { + const typename CFI_Parser::CIE_Info &cieInfo, + typename R::link_hardened_reg_arg_t pc, uintptr_t dso_base) { typename CFI_Parser::PrologInfo prolog; - if (CFI_Parser::parseFDEInstructions(_addressSpace, fdeInfo, cieInfo, pc, - R::getArch(), &prolog)) { + if (CFI_Parser::template parseFDEInstructions( + _addressSpace, fdeInfo, cieInfo, pc, R::getArch(), &prolog)) { // Save off parsed FDE info _info.start_ip = fdeInfo.pcStart; _info.end_ip = fdeInfo.pcEnd; @@ -1755,7 +1759,7 @@ bool UnwindCursor::getInfoFromFdeCie( template bool UnwindCursor::getInfoFromDwarfSection( - const typename R::link_reg_t &pc, const UnwindInfoSections §s, + typename R::link_hardened_reg_arg_t pc, const UnwindInfoSections §s, uint32_t fdeSectionOffsetHint) { typename CFI_Parser::FDE_Info fdeInfo; typename CFI_Parser::CIE_Info cieInfo; @@ -1763,34 +1767,33 @@ bool UnwindCursor::getInfoFromDwarfSection( bool foundInCache = false; // If compact encoding table gave offset into dwarf section, go directly there if (fdeSectionOffsetHint != 0) { - foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, - sects.dwarf_section_length, - sects.dwarf_section + fdeSectionOffsetHint, - &fdeInfo, &cieInfo); + foundFDE = CFI_Parser::template findFDE( + _addressSpace, pc, sects.dwarf_section, sects.dwarf_section_length, + sects.dwarf_section + fdeSectionOffsetHint, &fdeInfo, &cieInfo); } #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) if (!foundFDE && (sects.dwarf_index_section != 0)) { - foundFDE = EHHeaderParser::findFDE( + foundFDE = EHHeaderParser::template findFDE( _addressSpace, pc, sects.dwarf_index_section, (uint32_t)sects.dwarf_index_section_length, &fdeInfo, &cieInfo); } #endif if (!foundFDE) { // otherwise, search cache of previously found FDEs. - pint_t cachedFDE = DwarfFDECache::findFDE(sects.dso_base, pc); + pint_t cachedFDE = + DwarfFDECache::template findFDE(sects.dso_base, pc); if (cachedFDE != 0) { - foundFDE = - CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, - sects.dwarf_section_length, - cachedFDE, &fdeInfo, &cieInfo); + foundFDE = CFI_Parser::template findFDE( + _addressSpace, pc, sects.dwarf_section, sects.dwarf_section_length, + cachedFDE, &fdeInfo, &cieInfo); foundInCache = foundFDE; } } if (!foundFDE) { // Still not found, do full scan of __eh_frame section. - foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, - sects.dwarf_section_length, 0, - &fdeInfo, &cieInfo); + foundFDE = CFI_Parser::template findFDE( + _addressSpace, pc, sects.dwarf_section, sects.dwarf_section_length, 0, + &fdeInfo, &cieInfo); } if (foundFDE) { if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, sects.dso_base)) { @@ -1815,7 +1818,7 @@ bool UnwindCursor::getInfoFromDwarfSection( #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) template bool UnwindCursor::getInfoFromCompactEncodingSection( - const typename R::link_reg_t &pc, const UnwindInfoSections §s) { + typename R::link_hardened_reg_arg_t pc, const UnwindInfoSections §s) { const bool log = false; if (log) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n", @@ -2773,7 +2776,7 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { // Ask address space object to find unwind sections for this pc. UnwindInfoSections sects; - if (_addressSpace.findUnwindSections(pc, sects)) { + if (_addressSpace.template findUnwindSections(pc, sects)) { #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) // If there is a compact unwind encoding table, look there first. if (sects.compact_unwind_section != 0) { @@ -2829,8 +2832,8 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) // There is no static unwind info for this pc. Look to see if an FDE was // dynamically registered for it. - pint_t cachedFDE = DwarfFDECache::findFDE(DwarfFDECache::kSearchAll, - pc); + pint_t cachedFDE = + DwarfFDECache::template findFDE(DwarfFDECache::kSearchAll, pc); if (cachedFDE != 0) { typename CFI_Parser::FDE_Info fdeInfo; typename CFI_Parser::CIE_Info cieInfo; @@ -2842,7 +2845,7 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { // Lastly, ask AddressSpace object about platform specific ways to locate // other FDEs. pint_t fde; - if (_addressSpace.findOtherFDE(pc, fde)) { + if (_addressSpace.template findOtherFDE(pc, fde)) { typename CFI_Parser::FDE_Info fdeInfo; typename CFI_Parser::CIE_Info cieInfo; if (!CFI_Parser::decodeFDE(_addressSpace, fde, &fdeInfo, &cieInfo)) { @@ -3312,7 +3315,7 @@ bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, #else typename R::link_reg_t pc = this->getReg(UNW_REG_IP); #endif - return _addressSpace.findFunctionName(pc, buf, bufLen, offset); + return _addressSpace.template findFunctionName(pc, buf, bufLen, offset); } #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN)