diff --git a/compiler-rt/lib/builtins/gcc_personality_v0.c b/compiler-rt/lib/builtins/gcc_personality_v0.c index ef63a5fb83472..8bc7cb3ea7054 100644 --- a/compiler-rt/lib/builtins/gcc_personality_v0.c +++ b/compiler-rt/lib/builtins/gcc_personality_v0.c @@ -30,6 +30,46 @@ EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *, PCONTEXT, _Unwind_Personality_Fn); #endif +#if __has_include() +#include +#endif + +#if defined(__APPLE__) && __has_feature(ptrauth_qualifier) +#if __has_feature(ptrauth_restricted_intptr_qualifier) +#define PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(key, addressDiscriminated, \ + discriminatorString) \ + __ptrauth_restricted_intptr( \ + key, addressDiscriminated, \ + ptrauth_string_discriminator(discriminatorString)) +#else +#define PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(key, addressDiscriminated, \ + discriminatorString) \ + __ptrauth(key, addressDiscriminated, \ + ptrauth_string_discriminator(discriminatorString)) +#endif +#else +#define PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(key, addressDiscriminated, \ + discriminatorString) +#endif + +// Helper wrappers for pointer auth qualifiers because we use a lot of variants +// Suffixes: +// * PDC : ptrauth_key_process_dependent_code +// * RA : ptrauth_key_return_address +// * FN : ptrauth_key_function_pointer +#define PERSONALITY_PTRAUTH_RI_FN(__discriminator) \ + PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, \ + /*__address_discriminated=*/1, \ + __discriminator) +#define PERSONALITY_PTRAUTH_RI_PDC(__discriminator) \ + PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_code, \ + /*__address_discriminated=*/1, \ + __discriminator) +#define PERSONALITY_PTRAUTH_RI_RA(__discriminator) \ + PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_return_address, \ + /*__address_discriminated=*/1, \ + __discriminator) + // Pointer encodings documented at: // http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html @@ -205,7 +245,8 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( return continueUnwind(exceptionObject, context); uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1; - uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context); + uintptr_t PERSONALITY_PTRAUTH_RI_FN("__gcc_personality_v0'funcStart") + funcStart = (uintptr_t)_Unwind_GetRegionStart(context); uintptr_t pcOffset = pc - funcStart; // Parse LSDA header. @@ -224,11 +265,14 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength; const uint8_t *p = callSiteTableStart; while (p < callSiteTableEnd) { - uintptr_t start = readEncodedPointer(&p, callSiteEncoding); - size_t length = readEncodedPointer(&p, callSiteEncoding); - size_t landingPad = readEncodedPointer(&p, callSiteEncoding); + uintptr_t PERSONALITY_PTRAUTH_RI_PDC("__gcc_personality_v0'start") start = + readEncodedPointer(&p, callSiteEncoding); + size_t PERSONALITY_PTRAUTH_RI_PDC("__gcc_personality_v0'length") length = + readEncodedPointer(&p, callSiteEncoding); + size_t PERSONALITY_PTRAUTH_RI_PDC("__gcc_personality_v0'landingPadOffset") + landingPadOffset = readEncodedPointer(&p, callSiteEncoding); readULEB128(&p); // action value not used for C code - if (landingPad == 0) + if (landingPadOffset == 0) continue; // no landing pad for this entry if ((start <= pcOffset) && (pcOffset < (start + length))) { // Found landing pad for the PC. @@ -238,7 +282,20 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); - _Unwind_SetIP(context, (funcStart + landingPad)); +#define LANDING_PAD_DISCRIMINATOR "__gcc_personality_v0'landingPad" + size_t PERSONALITY_PTRAUTH_RI_RA(LANDING_PAD_DISCRIMINATOR) landingPad = + funcStart + landingPadOffset; +#if defined(__APPLE__) && __has_feature(ptrauth_qualifier) + uintptr_t stack_pointer = _Unwind_GetGR(context, -2); + const uintptr_t existingDiscriminator = ptrauth_blend_discriminator( + &landingPad, ptrauth_string_discriminator(LANDING_PAD_DISCRIMINATOR)); + uintptr_t newIP = (uintptr_t)ptrauth_auth_and_resign( + *(void **)&landingPad, ptrauth_key_function_pointer, + existingDiscriminator, ptrauth_key_return_address, stack_pointer); + _Unwind_SetIP(context, newIP); +#else + _Unwind_SetIP(context, landingPad); +#endif return _URC_INSTALL_CONTEXT; } } diff --git a/compiler-rt/lib/profile/InstrProfilingValue.c b/compiler-rt/lib/profile/InstrProfilingValue.c index a608d41d39e77..eea7b6ef46663 100644 --- a/compiler-rt/lib/profile/InstrProfilingValue.c +++ b/compiler-rt/lib/profile/InstrProfilingValue.c @@ -83,7 +83,13 @@ __llvm_profile_iterate_data(const __llvm_profile_data *Data) { /* This method is only used in value profiler mock testing. */ COMPILER_RT_VISIBILITY void * __llvm_get_function_addr(const __llvm_profile_data *Data) { - return Data->FunctionPointer; + void *FP = Data->FunctionPointer; +#if __has_feature(ptrauth_calls) + // This is only used for tests where we compare against what happens to be + // signed pointers. + FP = ptrauth_sign_unauthenticated(FP, VALID_CODE_KEY, 0); +#endif + return FP; } /* Allocate an array that holds the pointers to the linked lists of diff --git a/libcxxabi/include/__cxxabi_config.h b/libcxxabi/include/__cxxabi_config.h index 759445dac91f9..0a3c132e4baf0 100644 --- a/libcxxabi/include/__cxxabi_config.h +++ b/libcxxabi/include/__cxxabi_config.h @@ -32,22 +32,23 @@ #endif #if defined(_WIN32) - #if defined(_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) || (defined(__MINGW32__) && !defined(_LIBCXXABI_BUILDING_LIBRARY)) - #define _LIBCXXABI_HIDDEN - #define _LIBCXXABI_DATA_VIS - #define _LIBCXXABI_FUNC_VIS - #define _LIBCXXABI_TYPE_VIS - #elif defined(_LIBCXXABI_BUILDING_LIBRARY) - #define _LIBCXXABI_HIDDEN - #define _LIBCXXABI_DATA_VIS __declspec(dllexport) - #define _LIBCXXABI_FUNC_VIS __declspec(dllexport) - #define _LIBCXXABI_TYPE_VIS __declspec(dllexport) - #else - #define _LIBCXXABI_HIDDEN - #define _LIBCXXABI_DATA_VIS __declspec(dllimport) - #define _LIBCXXABI_FUNC_VIS __declspec(dllimport) - #define _LIBCXXABI_TYPE_VIS __declspec(dllimport) - #endif +# if defined(_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) || \ + (defined(__MINGW32__) && !defined(_LIBCXXABI_BUILDING_LIBRARY)) +# define _LIBCXXABI_HIDDEN +# define _LIBCXXABI_DATA_VIS +# define _LIBCXXABI_FUNC_VIS +# define _LIBCXXABI_TYPE_VIS +# elif defined(_LIBCXXABI_BUILDING_LIBRARY) +# define _LIBCXXABI_HIDDEN +# define _LIBCXXABI_DATA_VIS __declspec(dllexport) +# define _LIBCXXABI_FUNC_VIS __declspec(dllexport) +# define _LIBCXXABI_TYPE_VIS __declspec(dllexport) +# else +# define _LIBCXXABI_HIDDEN +# define _LIBCXXABI_DATA_VIS __declspec(dllimport) +# define _LIBCXXABI_FUNC_VIS __declspec(dllimport) +# define _LIBCXXABI_TYPE_VIS __declspec(dllimport) +# endif #else #if !defined(_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) #define _LIBCXXABI_HIDDEN __attribute__((__visibility__("hidden"))) @@ -109,4 +110,38 @@ # define _LIBCXXABI_NOEXCEPT noexcept #endif +#if __has_include() +# include +#endif + +#if defined(__APPLE__) && __has_feature(ptrauth_qualifier) +# define _LIBCXXABI_PTRAUTH(__key, __address_discriminated, __discriminator) \ + __ptrauth(__key, __address_discriminated, ptrauth_string_discriminator(__discriminator)) +// This work around is required to support divergence in spelling +// during the ptrauth upstreaming process. +# if __has_feature(ptrauth_restricted_intptr_qualifier) +# define _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, __discriminator) \ + __ptrauth_restricted_intptr(__key, __address_discriminated, ptrauth_string_discriminator(__discriminator)) +# else +# define _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, __discriminator) \ + __ptrauth(__key, __address_discriminated, ptrauth_string_discriminator(__discriminator)) +# endif +#else +# define _LIBCXXABI_PTRAUTH(__key, __address_discriminated, __discriminator) +# define _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, __discriminator) +#endif + +// Helper wrappers for pointer auth qualifiers because we use a lot of variants +// Suffixes: +// * _RI : qualifier is __ptrauth_restricted_intptr +// * PDD : key is ptrauth_key_process_dependent_data +// * FN : key is ptrauth_key_function_pointer +#define _LIBCXXABI_PTRAUTH_PDD(__discriminator) \ + _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data, /*__address_discriminated=*/1, __discriminator) +#define _LIBCXXABI_PTRAUTH_FN(__discriminator) \ + _LIBCXXABI_PTRAUTH(ptrauth_key_function_pointer, /*__address_discriminated=*/1, __discriminator) +#define _LIBCXXABI_PTRAUTH_RI_PDD(__discriminator) \ + _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, /*__address_discriminated=*/1, \ + __discriminator) + #endif // ____CXXABI_CONFIG_H diff --git a/libcxxabi/src/cxa_exception.h b/libcxxabi/src/cxa_exception.h index aba08f2992103..6b0db16afe44a 100644 --- a/libcxxabi/src/cxa_exception.h +++ b/libcxxabi/src/cxa_exception.h @@ -47,10 +47,11 @@ struct _LIBCXXABI_HIDDEN __cxa_exception { // In Wasm, a destructor returns its argument void *(_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); #else - void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); + void(_LIBCXXABI_DTOR_FUNC* _LIBCXXABI_PTRAUTH_FN("__cxa_exception::exceptionDestructor") + exceptionDestructor)(void*); #endif - std::unexpected_handler unexpectedHandler; - std::terminate_handler terminateHandler; + std::unexpected_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::unexpectedHandler") unexpectedHandler; + std::terminate_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::terminateHandler") terminateHandler; __cxa_exception *nextException; @@ -61,10 +62,10 @@ struct _LIBCXXABI_HIDDEN __cxa_exception { int propagationCount; #else int handlerSwitchValue; - const unsigned char *actionRecord; - const unsigned char *languageSpecificData; - void *catchTemp; - void *adjustedPtr; + const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::actionRecord") actionRecord; + const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::languageSpecificData") languageSpecificData; + void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::catchTemp") catchTemp; + void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::adjustedPtr") adjustedPtr; #endif #if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI) @@ -79,6 +80,8 @@ struct _LIBCXXABI_HIDDEN __cxa_exception { // http://sourcery.mentor.com/archives/cxx-abi-dev/msg01924.html // The layout of this structure MUST match the layout of __cxa_exception, with // primaryException instead of referenceCount. +// The tags used in the pointer authentication qualifiers also need to match +// those of the corresponding members in __cxa_exception. struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { #if defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI) void* reserve; // padding. @@ -86,9 +89,10 @@ struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { #endif std::type_info *exceptionType; - void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *); - std::unexpected_handler unexpectedHandler; - std::terminate_handler terminateHandler; + void(_LIBCXXABI_DTOR_FUNC* _LIBCXXABI_PTRAUTH_FN("__cxa_exception::exceptionDestructor") + exceptionDestructor)(void*); + std::unexpected_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::unexpectedHandler") unexpectedHandler; + std::terminate_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::terminateHandler") terminateHandler; __cxa_exception *nextException; @@ -99,10 +103,10 @@ struct _LIBCXXABI_HIDDEN __cxa_dependent_exception { int propagationCount; #else int handlerSwitchValue; - const unsigned char *actionRecord; - const unsigned char *languageSpecificData; - void * catchTemp; - void *adjustedPtr; + const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::actionRecord") actionRecord; + const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::languageSpecificData") languageSpecificData; + void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::catchTemp") catchTemp; + void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::adjustedPtr") adjustedPtr; #endif #if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI) diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp index 5f6e75c5be19c..05737b37688da 100644 --- a/libcxxabi/src/cxa_personality.cpp +++ b/libcxxabi/src/cxa_personality.cpp @@ -22,6 +22,12 @@ #include "private_typeinfo.h" #include "unwind.h" +#if __has_include() +# include +#endif + +#include "libunwind.h" + // TODO: This is a temporary workaround for libc++abi to recognize that it's being // built against LLVM's libunwind. LLVM's libunwind started reporting _LIBUNWIND_VERSION // in LLVM 15 -- we can remove this workaround after shipping LLVM 17. Once we remove @@ -527,12 +533,19 @@ get_thrown_object_ptr(_Unwind_Exception* unwind_exception) namespace { +#define _LIBCXXABI_PTRAUTH_KEY ptrauth_key_process_dependent_code +typedef const uint8_t* _LIBCXXABI_PTRAUTH_PDD("scan_results::languageSpecificData") lsd_ptr_t; +typedef const uint8_t* _LIBCXXABI_PTRAUTH_PDD("scan_results::actionRecord") action_ptr_t; +#define _LIBCXXABI_PTRAUTH_SCANRESULT_LANDINGPAD_DISC "scan_results::landingPad" +typedef uintptr_t _LIBCXXABI_PTRAUTH_RI_PDD(_LIBCXXABI_PTRAUTH_SCANRESULT_LANDINGPAD_DISC) landing_pad_t; +typedef void* _LIBCXXABI_PTRAUTH_PDD(_LIBCXXABI_PTRAUTH_SCANRESULT_LANDINGPAD_DISC) landing_pad_ptr_t; + struct scan_results { int64_t ttypeIndex; // > 0 catch handler, < 0 exception spec handler, == 0 a cleanup - const uint8_t* actionRecord; // Currently unused. Retained to ease future maintenance. - const uint8_t* languageSpecificData; // Needed only for __cxa_call_unexpected - uintptr_t landingPad; // null -> nothing found, else something found + action_ptr_t actionRecord; // Currently unused. Retained to ease future maintenance. + lsd_ptr_t languageSpecificData; // Needed only for __cxa_call_unexpected + landing_pad_t landingPad; // null -> nothing found, else something found void* adjustedPtr; // Used in cxa_exception.cpp _Unwind_Reason_Code reason; // One of _URC_FATAL_PHASE1_ERROR, // _URC_FATAL_PHASE2_ERROR, @@ -541,7 +554,33 @@ struct scan_results }; } // unnamed namespace +} +namespace { +// The logical model for casting authenticated function pointers makes +// it impossible to directly cast them without breaking the authentication, +// as a result we need this pair of helpers. +template +void set_landing_pad_as_ptr(scan_results& results, const PtrType& out) { + union { + landing_pad_t* as_landing_pad; + landing_pad_ptr_t* as_pointer; + } u; + u.as_landing_pad = &results.landingPad; + *u.as_pointer = out; +} + +static const landing_pad_ptr_t& get_landing_pad_as_ptr(const scan_results& results) { + union { + const landing_pad_t* as_landing_pad; + const landing_pad_ptr_t* as_pointer; + } u; + u.as_landing_pad = &results.landingPad; + return *u.as_pointer; +} +} // unnamed namespace + +extern "C" { static void set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context, @@ -557,7 +596,19 @@ set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context, reinterpret_cast(unwind_exception)); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), static_cast(results.ttypeIndex)); +#if defined(__APPLE__) && __has_feature(ptrauth_qualifier) + auto stack_pointer = _Unwind_GetGR(context, UNW_REG_SP); + // We manually re-sign the IP as the __ptrauth qualifiers cannot + // express the required relationship with the destination address + const auto existingDiscriminator = ptrauth_blend_discriminator( + &results.landingPad, ptrauth_string_discriminator(_LIBCXXABI_PTRAUTH_SCANRESULT_LANDINGPAD_DISC)); + unw_word_t newIP = + (unw_word_t)ptrauth_auth_and_resign(*(void**)&results.landingPad, _LIBCXXABI_PTRAUTH_KEY, existingDiscriminator, + ptrauth_key_return_address, stack_pointer); + _Unwind_SetIP(context, newIP); +#else _Unwind_SetIP(context, results.landingPad); +#endif } /* @@ -691,12 +742,12 @@ static void scan_eh_tab(scan_results &results, _Unwind_Action actions, // The call sites are ordered in increasing value of start uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding); uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding); - uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); + landing_pad_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); uintptr_t actionEntry = readULEB128(&callSitePtr); if ((start <= ipOffset) && (ipOffset < (start + length))) #else // __USING_SJLJ_EXCEPTIONS__ || __WASM_EXCEPTIONS__ // ip is 1-based index into this table - uintptr_t landingPad = readULEB128(&callSitePtr); + landing_pad_t landingPad = readULEB128(&callSitePtr); uintptr_t actionEntry = readULEB128(&callSitePtr); if (--ip == 0) #endif // __USING_SJLJ_EXCEPTIONS__ || __WASM_EXCEPTIONS__ @@ -935,8 +986,7 @@ __gxx_personality_v0 results.ttypeIndex = exception_header->handlerSwitchValue; results.actionRecord = exception_header->actionRecord; results.languageSpecificData = exception_header->languageSpecificData; - results.landingPad = - reinterpret_cast(exception_header->catchTemp); + set_landing_pad_as_ptr(results, exception_header->catchTemp); results.adjustedPtr = exception_header->adjustedPtr; // Jump to the handler. @@ -970,7 +1020,7 @@ __gxx_personality_v0 exc->handlerSwitchValue = static_cast(results.ttypeIndex); exc->actionRecord = results.actionRecord; exc->languageSpecificData = results.languageSpecificData; - exc->catchTemp = reinterpret_cast(results.landingPad); + exc->catchTemp = get_landing_pad_as_ptr(results); exc->adjustedPtr = results.adjustedPtr; #ifdef __WASM_EXCEPTIONS__ // Wasm only uses a single phase (_UA_SEARCH_PHASE), so save the diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h index b2dae8feed9a3..4c6b031d992c8 100644 --- a/libunwind/include/libunwind.h +++ b/libunwind/include/libunwind.h @@ -43,6 +43,60 @@ #define LIBUNWIND_AVAIL #endif +#if __has_include() +#include +#endif + +#if defined(__APPLE__) && __has_feature(ptrauth_qualifier) +#define _LIBUNWIND_PTRAUTH(__key, __address_discriminated, __discriminator) \ + __ptrauth(__key, __address_discriminated, \ + ptrauth_string_discriminator(__discriminator)) +// This work around is required to support divergence in spelling +// developed during the ptrauth upstreaming process. +#if __has_feature(ptrauth_restricted_intptr_qualifier) +#define _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, \ + __discriminator) \ + __ptrauth_restricted_intptr(__key, __address_discriminated, \ + ptrauth_string_discriminator(__discriminator)) +#else +#define _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, \ + __discriminator) \ + __ptrauth(__key, __address_discriminated, \ + ptrauth_string_discriminator(__discriminator)) +#endif +#else +#define _LIBUNWIND_PTRAUTH(__key, __address_discriminated, __discriminator) +#define _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, \ + __discriminator) +#endif + +// Helper wrappers for pointer auth qualifiers because we use a lot of variants +// Suffixes: +// * RI_*: __ptrauth_restricted_intptr +// * PDD : ptrauth_key_process_dependent_data +// * PIC : ptrauth_key_process_independent_code +// * PDC : ptrauth_key_process_dependent_code +// * FN : ptrauth_key_function_pointer +#define __LIBUNWIND_PTRAUTH_RI_PDD(__discriminator) \ + _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data, \ + /*__address_discriminated=*/1, \ + __discriminator) +#define __LIBUNWIND_PTRAUTH_RI_PIC(__discriminator) \ + _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_independent_code, \ + /*__address_discriminated=*/1, \ + __discriminator) +#define __LIBUNWIND_PTRAUTH_RI_PDC(__discriminator) \ + _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_code, \ + /*__address_discriminated=*/1, \ + __discriminator) +#define __LIBUNWIND_PTRAUTH_RI_FN(__discriminator) \ + _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer, \ + /*__address_discriminated=*/1, \ + __discriminator) +#define __LIBUNWIND_PTRAUTH_FN(__discriminator) \ + _LIBUNWIND_PTRAUTH(ptrauth_key_function_pointer, \ + /*__address_discriminated=*/1, __discriminator) + #if defined(_WIN32) && defined(__SEH__) #define LIBUNWIND_CURSOR_ALIGNMENT_ATTR __attribute__((__aligned__(16))) #else @@ -88,17 +142,24 @@ typedef double unw_fpreg_t; #endif struct unw_proc_info_t { - unw_word_t start_ip; /* start address of function */ - unw_word_t end_ip; /* address after end of function */ - unw_word_t lsda; /* address of language specific data area, */ - /* or zero if not used */ - unw_word_t handler; /* personality routine, or zero if not used */ + unw_word_t __LIBUNWIND_PTRAUTH_RI_PIC( + "unw_proc_info_t::start_ip") start_ip; /* start address of function */ + unw_word_t __LIBUNWIND_PTRAUTH_RI_PIC( + "unw_proc_info_t::end_ip") end_ip; /* address after end of function */ + unw_word_t __LIBUNWIND_PTRAUTH_RI_PDD("unw_proc_info_t::lsda") + lsda; /* address of language specific data area, */ + /* or zero if not used */ + unw_word_t __LIBUNWIND_PTRAUTH_RI_FN("unw_proc_info_t::handler") + handler; /* personality routine, or zero if not used */ unw_word_t gp; /* not used */ - unw_word_t flags; /* not used */ + unw_word_t + __LIBUNWIND_PTRAUTH_RI_PDD("unw_proc_info_t::flags") flags; /* not used */ uint32_t format; /* compact unwind encoding, or zero if none */ uint32_t unwind_info_size; /* size of DWARF unwind info, or zero if none */ - unw_word_t unwind_info; /* address of DWARF unwind info, or zero */ - unw_word_t extra; /* mach_header of mach-o image containing func */ + unw_word_t __LIBUNWIND_PTRAUTH_RI_PDD("unw_proc_info_t::unwind_info") + unwind_info; /* address of DWARF unwind info, or zero */ + unw_word_t __LIBUNWIND_PTRAUTH_RI_PDD("unw_proc_info_t::extra") + extra; /* mach_header of mach-o image containing func */ }; typedef struct unw_proc_info_t unw_proc_info_t; diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp index 5551c7d4bef1c..b4bca46124b09 100644 --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -129,26 +129,38 @@ struct UnwindInfoSections { defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) || \ defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) // No dso_base for SEH. - uintptr_t dso_base; + uintptr_t + __LIBUNWIND_PTRAUTH_RI_PDD("UnwindInfoSections::dso_base") dso_base = 0; #endif #if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) - size_t text_segment_length; + size_t __LIBUNWIND_PTRAUTH_RI_PDD("UnwindInfoSections::text_segment_length") + text_segment_length = 0; #endif #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) - uintptr_t dwarf_section; - size_t dwarf_section_length; + uintptr_t __LIBUNWIND_PTRAUTH_RI_PDD("UnwindInfoSections::dwarf_section") + dwarf_section = 0; + size_t __LIBUNWIND_PTRAUTH_RI_PDD("UnwindInfoSections::dwarf_section_length") + dwarf_section_length = 0; #endif #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) - uintptr_t dwarf_index_section; - size_t dwarf_index_section_length; + uintptr_t __LIBUNWIND_PTRAUTH_RI_PDD( + "UnwindInfoSections::dwarf_index_section") dwarf_index_section = 0; + size_t __LIBUNWIND_PTRAUTH_RI_PDD( + "UnwindInfoSections::dwarf_index_section_length") + dwarf_index_section_length = 0; #endif #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) - uintptr_t compact_unwind_section; - size_t compact_unwind_section_length; + uintptr_t __LIBUNWIND_PTRAUTH_RI_PDD( + "UnwindInfoSections::compact_unwind_section") compact_unwind_section = 0; + size_t __LIBUNWIND_PTRAUTH_RI_PDD( + "UnwindInfoSections::compact_unwind_section_length") + compact_unwind_section_length = 0; #endif #if defined(_LIBUNWIND_ARM_EHABI) - uintptr_t arm_section; - size_t arm_section_length; + uintptr_t __LIBUNWIND_PTRAUTH_RI_PDD("UnwindInfoSections::arm_section") + arm_section = 0; + size_t __LIBUNWIND_PTRAUTH_RI_PDD("UnwindInfoSections::arm_section_length") + arm_section_length = 0; #endif }; @@ -196,7 +208,7 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace { static int64_t getSLEB128(pint_t &addr, pint_t end); pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, - pint_t datarelBase = 0); + 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); @@ -269,7 +281,7 @@ inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) { inline LocalAddressSpace::pint_t LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, - pint_t datarelBase) { + pint_t datarelBase, pint_t *resultAddr) { pint_t startAddr = addr; const uint8_t *p = (uint8_t *)addr; pint_t result; @@ -353,8 +365,14 @@ LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, break; } - if (encoding & DW_EH_PE_indirect) + if (encoding & DW_EH_PE_indirect) { + if (resultAddr) + *resultAddr = result; result = getP(result); + } else { + if (resultAddr) + *resultAddr = startAddr; + } return result; } diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index e7be0d6d5d635..3c34ebb6b7a16 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -22,6 +22,9 @@ #include "dwarf2.h" #include "libunwind_ext.h" +#if __has_include() +#include +#endif namespace libunwind { @@ -34,8 +37,9 @@ class DwarfInstructions { typedef typename A::pint_t pint_t; typedef typename A::sint_t sint_t; - static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, - R ®isters, bool &isSignalFrame, bool stage2); + static int stepWithDwarf(A &addressSpace, typename R::link_reg_t &pc, + pint_t fdeStart, R ®isters, bool &isSignalFrame, + bool stage2); private: @@ -207,7 +211,8 @@ bool DwarfInstructions::isReturnAddressSignedWithPC(A &addressSpace, #endif template -int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, +int DwarfInstructions::stepWithDwarf(A &addressSpace, + typename R::link_reg_t &pc, pint_t fdeStart, R ®isters, bool &isSignalFrame, bool stage2) { FDE_Info fdeInfo; diff --git a/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp index 7e85025dd054d..bd12734fd47c4 100644 --- a/libunwind/src/DwarfParser.hpp +++ b/libunwind/src/DwarfParser.hpp @@ -23,6 +23,10 @@ #include "config.h" +#if __has_include() +#include +#endif + namespace libunwind { /// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. @@ -33,6 +37,8 @@ template class CFI_Parser { public: typedef typename A::pint_t pint_t; + typedef pint_t + __LIBUNWIND_PTRAUTH_RI_FN("CIE_Info::personality") personality_t; /// Information encoded in a CIE (Common Information Entry) struct CIE_Info { @@ -43,7 +49,7 @@ class CFI_Parser { uint8_t lsdaEncoding; uint8_t personalityEncoding; uint8_t personalityOffsetInCIE; - pint_t personality; + personality_t personality; uint32_t codeAlignFactor; int dataAlignFactor; bool isSignalFrame; @@ -313,6 +319,17 @@ bool CFI_Parser::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, } return false; } +namespace { +// This helper function handles setting the manually signed personality on +// CIE_Info without attempt to authenticate and/or re-sign +template +void set_cie_info_personality(CIE_Info *info, T signed_personality) { + static_assert(sizeof(info->personality) == sizeof(signed_personality), + "Signed personality is the wrong size"); + memmove((void *)&info->personality, (void *)&signed_personality, + sizeof(signed_personality)); +} +} // namespace /// Extract info from a CIE template @@ -369,6 +386,7 @@ const char *CFI_Parser::parseCIE(A &addressSpace, pint_t cie, cieInfo->returnAddressRegister = (uint8_t)raReg; // parse augmentation data based on augmentation string const char *result = NULL; + pint_t resultAddr = 0; if (addressSpace.get8(strStart) == 'z') { // parse augmentation data length addressSpace.getULEB128(p, cieContentEnd); @@ -377,13 +395,32 @@ const char *CFI_Parser::parseCIE(A &addressSpace, pint_t cie, case 'z': cieInfo->fdesHaveAugmentationData = true; break; - case 'P': + case 'P': { cieInfo->personalityEncoding = addressSpace.get8(p); ++p; cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie); - cieInfo->personality = addressSpace - .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); + pint_t personality = addressSpace.getEncodedP( + p, cieContentEnd, cieInfo->personalityEncoding, + /*datarelBase=*/0, &resultAddr); +#if __has_feature(ptrauth_calls) + if (personality) { + // The GOT for the personality function was signed address + // authenticated. Manually re-sign with the CIE_Info::personality + // schema. If we could guarantee the encoding of the personality we + // could avoid this by simply giving resultAddr the correct ptrauth + // schema and performing an assignment. + const auto discriminator = ptrauth_blend_discriminator( + &cieInfo->personality, + ptrauth_string_discriminator("CIE_Info::personality")); + void *signedPtr = ptrauth_auth_and_resign( + (void *)personality, ptrauth_key_function_pointer, resultAddr, + ptrauth_key_function_pointer, discriminator); + personality = (pint_t)signedPtr; + } +#endif + set_cie_info_personality(cieInfo, personality); break; + } case 'L': cieInfo->lsdaEncoding = addressSpace.get8(p); ++p; diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp index 2c3bfb7e8428a..cc1f96a34e018 100644 --- a/libunwind/src/Registers.hpp +++ b/libunwind/src/Registers.hpp @@ -17,8 +17,13 @@ #include "config.h" #include "libunwind.h" +#include "libunwind_ext.h" #include "shadow_stack_unwind.h" +#if __has_include() +#include +#endif + namespace libunwind { // For emulating 128-bit registers @@ -93,6 +98,13 @@ class _LIBUNWIND_HIDDEN Registers_x86 { uint32_t getEDI() const { return _registers.__edi; } void setEDI(uint32_t value) { _registers.__edi = value; } + typedef uint32_t reg_t; + typedef uint32_t link_reg_t; + void loadAndAuthenticateLinkRegister(reg_t srcLinkRegister, + link_reg_t *dstLinkRegister) { + *dstLinkRegister = srcLinkRegister; + } + private: struct GPRs { unsigned int __eax; @@ -311,6 +323,13 @@ class _LIBUNWIND_HIDDEN Registers_x86_64 { uint64_t getR15() const { return _registers.__r15; } void setR15(uint64_t value) { _registers.__r15 = value; } + typedef uint64_t reg_t; + typedef uint64_t link_reg_t; + void loadAndAuthenticateLinkRegister(reg_t srcLinkRegister, + link_reg_t *dstLinkRegister) { + *dstLinkRegister = srcLinkRegister; + } + private: struct GPRs { uint64_t __rax; @@ -622,6 +641,13 @@ class _LIBUNWIND_HIDDEN Registers_ppc { uint64_t getLR() const { return _registers.__lr; } void setLR(uint32_t value) { _registers.__lr = value; } + typedef uint32_t reg_t; + typedef uint32_t link_reg_t; + void loadAndAuthenticateLinkRegister(reg_t srcLinkRegister, + link_reg_t *dstLinkRegister) { + *dstLinkRegister = srcLinkRegister; + } + private: struct ppc_thread_state_t { unsigned int __srr0; /* Instruction address register (PC) */ @@ -1845,10 +1871,53 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { uint64_t getSP() const { return _registers.__sp; } void setSP(uint64_t value) { _registers.__sp = value; } - uint64_t getIP() const { return _registers.__pc; } - void setIP(uint64_t value) { _registers.__pc = value; } - uint64_t getFP() const { return _registers.__fp; } - void setFP(uint64_t value) { _registers.__fp = value; } + uint64_t getIP() const { + uint64_t value = _registers.__pc; +#if __has_feature(ptrauth_calls) + // Note the value of the PC was signed to its address in the register state + // but everyone else expects it to be sign by the SP, so convert on return. + value = (uint64_t)ptrauth_auth_and_resign( + (void *)_registers.__pc, ptrauth_key_return_address, &_registers.__pc, + ptrauth_key_return_address, getSP()); +#endif + return value; + } + void setIP(uint64_t value) { +#if __has_feature(ptrauth_calls) + // Note the value which was set should have been signed with the SP. + // We then resign with the slot we are being stored in to so that both SP + // and LR can't be spoofed at the same time. + value = (uint64_t)ptrauth_auth_and_resign( + (void *)value, ptrauth_key_return_address, getSP(), + ptrauth_key_return_address, &_registers.__pc); +#endif + _registers.__pc = value; + } + uint64_t getFP() const { return _registers.__fp; } + void setFP(uint64_t value) { _registers.__fp = value; } + + typedef uint64_t reg_t; + typedef uint64_t + __LIBUNWIND_PTRAUTH_RI_PDC("Registers_arm64::link_reg_t") link_reg_t; + void + loadAndAuthenticateLinkRegister(reg_t inplaceAuthedLinkRegister, + link_reg_t *referenceAuthedLinkRegister) { +#if __has_feature(ptrauth_calls) + // If we are in an arm64e frame, then the PC should have been signed + // with the SP + *referenceAuthedLinkRegister = (uint64_t)ptrauth_auth_data( + (void *)inplaceAuthedLinkRegister, ptrauth_key_return_address, + _registers.__sp); +#else + *referenceAuthedLinkRegister = inplaceAuthedLinkRegister; +#endif + } + + // arm64_32 and i386 simulator hack + void loadAndAuthenticateLinkRegister(uint32_t srcLinkRegister, + uint32_t *dstLinkRegister) { + *dstLinkRegister = srcLinkRegister; + } private: struct GPRs { @@ -1877,6 +1946,32 @@ inline Registers_arm64::Registers_arm64(const void *registers) { memcpy(_vectorHalfRegisters, static_cast(registers) + sizeof(GPRs), sizeof(_vectorHalfRegisters)); +#if __has_feature(ptrauth_calls) + uint64_t pcRegister = 0; + memcpy(&pcRegister, ((uint8_t *)&_registers) + offsetof(GPRs, __pc), + sizeof(pcRegister)); + setIP(pcRegister); + uint64_t fpRegister = 0; + memcpy(&fpRegister, ((uint8_t *)&_registers) + offsetof(GPRs, __fp), + sizeof(fpRegister)); + setFP(fpRegister); +#endif +} + +inline Registers_arm64::Registers_arm64(const Registers_arm64 &other) { + *this = other; +} + +inline Registers_arm64 & +Registers_arm64::operator=(const Registers_arm64 &other) { + memcpy(&_registers, &other._registers, sizeof(_registers)); + memcpy(_vectorHalfRegisters, &other._vectorHalfRegisters, + sizeof(_vectorHalfRegisters)); +#if __has_feature(ptrauth_calls) + setIP(other.getIP()); + setFP(other.getFP()); +#endif + return *this; } inline Registers_arm64::Registers_arm64() { @@ -2140,6 +2235,13 @@ class _LIBUNWIND_HIDDEN Registers_arm { uint32_t getIP() const { return _registers.__pc; } void setIP(uint32_t value) { _registers.__pc = value; } + typedef uint32_t reg_t; + typedef uint32_t link_reg_t; + void loadAndAuthenticateLinkRegister(reg_t srcLinkRegister, + link_reg_t *dstLinkRegister) { + *dstLinkRegister = srcLinkRegister; + } + void saveVFPAsX() { assert(_use_X_for_vfp_save || !_saved_vfp_d0_d15); _use_X_for_vfp_save = true; diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 55db035e62040..feae9263df882 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -111,6 +111,10 @@ extern "C" _Unwind_Reason_Code __libunwind_seh_personality( #endif +#if __has_include() +#include +#endif + namespace libunwind { #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) @@ -1046,18 +1050,22 @@ class UnwindCursor : public AbstractUnwindCursor{ 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(pint_t pc, const UnwindInfoSections §s, - uint32_t fdeSectionOffsetHint=0); + bool getInfoFromDwarfSection(const typename R::link_reg_t &pc, + const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint = 0); int stepWithDwarfFDE(bool stage2) { + typename R::reg_t rawPC = this->getReg(UNW_REG_IP); + typename R::link_reg_t pc; + _registers.loadAndAuthenticateLinkRegister(rawPC, &pc); return DwarfInstructions::stepWithDwarf( - _addressSpace, (pint_t)this->getReg(UNW_REG_IP), - (pint_t)_info.unwind_info, _registers, _isSignalFrame, stage2); + _addressSpace, pc, (pint_t)_info.unwind_info, _registers, + _isSignalFrame, stage2); } #endif #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) - bool getInfoFromCompactEncodingSection(pint_t pc, - const UnwindInfoSections §s); + bool getInfoFromCompactEncodingSection(const typename R::link_reg_t &pc, + const UnwindInfoSections §s); int stepWithCompactEncoding(bool stage2 = false) { #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) if ( compactSaysUseDwarf() ) @@ -1682,9 +1690,9 @@ bool UnwindCursor::getInfoFromFdeCie( } template -bool UnwindCursor::getInfoFromDwarfSection(pint_t pc, - const UnwindInfoSections §s, - uint32_t fdeSectionOffsetHint) { +bool UnwindCursor::getInfoFromDwarfSection( + const typename R::link_reg_t &pc, const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint) { typename CFI_Parser::FDE_Info fdeInfo; typename CFI_Parser::CIE_Info cieInfo; bool foundFDE = false; @@ -1741,9 +1749,21 @@ bool UnwindCursor::getInfoFromDwarfSection(pint_t pc, #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) +// This helper function handles setting the manually signed handler on +// unw_proc_info without attempt to authenticate and/or re-sign +namespace { +template +void set_proc_info_handler(unw_proc_info_t &info, T signed_handler) { + static_assert(sizeof(info.handler) == sizeof(signed_handler), + "Signed handler is the wrong size"); + memmove((void *)&info.handler, (void *)&signed_handler, + sizeof(signed_handler)); +} +} // unnamed namespace + template -bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, - const UnwindInfoSections §s) { +bool UnwindCursor::getInfoFromCompactEncodingSection( + const typename R::link_reg_t &pc, const UnwindInfoSections §s) { const bool log = false; if (log) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n", @@ -1974,6 +1994,17 @@ bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, personalityIndex * sizeof(uint32_t)); pint_t personalityPointer = sects.dso_base + (pint_t)personalityDelta; personality = _addressSpace.getP(personalityPointer); +#if __has_feature(ptrauth_calls) + // The GOT for the personality function was signed address authenticated. + // Resign is as a regular function pointer. + const auto discriminator = ptrauth_blend_discriminator( + &_info.handler, + ptrauth_string_discriminator("unw_proc_info_t::handler")); + void *signedPtr = ptrauth_auth_and_resign( + (void *)personality, ptrauth_key_function_pointer, personalityPointer, + ptrauth_key_function_pointer, discriminator); + personality = (__typeof(personality))signedPtr; +#endif if (log) fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " "personalityDelta=0x%08X, personality=0x%08llX\n", @@ -1987,7 +2018,7 @@ bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, _info.start_ip = funcStart; _info.end_ip = funcEnd; _info.lsda = lsda; - _info.handler = personality; + set_proc_info_handler(_info, personality); _info.gp = 0; _info.flags = 0; _info.format = encoding; @@ -2640,13 +2671,17 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { _isSigReturn = false; #endif - pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + typename R::reg_t rawPC = this->getReg(UNW_REG_IP); + #if defined(_LIBUNWIND_ARM_EHABI) // Remove the thumb bit so the IP represents the actual instruction address. // This matches the behaviour of _Unwind_GetIP on arm. - pc &= (pint_t)~0x1; + rawPC &= (pint_t)~0x1; #endif + typename R::link_reg_t pc; + _registers.loadAndAuthenticateLinkRegister(rawPC, &pc); + // Exit early if at the top of the stack. if (pc == 0) { _unwindInfoMissing = true; @@ -3195,9 +3230,12 @@ void UnwindCursor::getInfo(unw_proc_info_t *info) { template bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, - unw_word_t *offset) { - return _addressSpace.findFunctionName((pint_t)this->getReg(UNW_REG_IP), - buf, bufLen, offset); + unw_word_t *offset) { + typename R::reg_t rawPC = this->getReg(UNW_REG_IP); + typename R::link_reg_t pc; + _registers.loadAndAuthenticateLinkRegister(rawPC, &pc); + + return _addressSpace.findFunctionName(pc, buf, bufLen, offset); } #if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) diff --git a/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c index a258a832a9c31..205265d184310 100644 --- a/libunwind/src/UnwindLevel1.c +++ b/libunwind/src/UnwindLevel1.c @@ -31,6 +31,10 @@ #include "shadow_stack_unwind.h" #include "unwind.h" +#if __has_include() +#include +#endif + #if !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ !defined(__wasm__) @@ -90,6 +94,19 @@ } while (0) #endif +// There is not currently a clean way to cast between an authenticated +// integer and an authenticated function pointer, so we need this helper +// function to keep things clean. +static _Unwind_Personality_Fn get_handler_function(unw_proc_info_t *frameInfo) { + union { + void *opaque_handler; + _Unwind_Personality_Fn __LIBUNWIND_PTRAUTH_FN("unw_proc_info_t::handler") * + handler; + } u; + u.opaque_handler = (void *)&frameInfo->handler; + return *u.handler; +} + static _Unwind_Reason_Code unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { __unw_init_local(cursor, uc); @@ -147,8 +164,7 @@ unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except // If there is a personality routine, ask it if it will want to stop at // this frame. if (frameInfo.handler != 0) { - _Unwind_Personality_Fn p = - (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); + _Unwind_Personality_Fn p = get_handler_function(&frameInfo); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase1(ex_obj=%p): calling personality function %p", (void *)exception_object, (void *)(uintptr_t)p); @@ -275,8 +291,7 @@ unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *except ++framesWalked; // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { - _Unwind_Personality_Fn p = - (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); + _Unwind_Personality_Fn p = get_handler_function(&frameInfo); _Unwind_Action action = _UA_CLEANUP_PHASE; if (sp == exception_object->private_2) { // Tell personality this was the frame it marked in phase 1. @@ -393,8 +408,7 @@ unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, ++framesWalked; // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { - _Unwind_Personality_Fn p = - (_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler); + _Unwind_Personality_Fn p = get_handler_function(&frameInfo); _LIBUNWIND_TRACE_UNWINDING( "unwind_phase2_forced(ex_obj=%p): calling personality function %p", (void *)exception_object, (void *)(uintptr_t)p); @@ -597,6 +611,18 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { unw_cursor_t *cursor = (unw_cursor_t *)context; unw_word_t result; __unw_get_reg(cursor, UNW_REG_IP, &result); + +#if __has_feature(ptrauth_calls) + // If we are in an arm64e frame, then the PC should have been signed with the + // sp + { + unw_word_t sp; + __unw_get_reg(cursor, UNW_REG_SP, &sp); + result = (unw_word_t)ptrauth_auth_data((void *)result, + ptrauth_key_return_address, sp); + } +#endif + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIxPTR, (void *)context, result); return (uintptr_t)result; diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S index 5e199188945df..632c662cc11e5 100644 --- a/libunwind/src/UnwindRegistersRestore.S +++ b/libunwind/src/UnwindRegistersRestore.S @@ -657,7 +657,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) ldp x24,x25, [x0, #0x0C0] ldp x26,x27, [x0, #0x0D0] ldp x28,x29, [x0, #0x0E0] - ldr x30, [x0, #0x100] // restore pc into lr + #if defined(__ARM_FP) && __ARM_FP != 0 ldp d0, d1, [x0, #0x110] ldp d2, d3, [x0, #0x120] @@ -681,7 +681,18 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) // context struct, because it is allocated on the stack, and an exception // could clobber the de-allocated portion of the stack after sp has been // restored. - ldr x16, [x0, #0x0F8] + + ldr x16, [x0, #0x0F8] // load sp into scratch + ldr lr, [x0, #0x100] // restore pc into lr + +#if __has_feature(ptrauth_calls) + // The LR is signed with its address inside the register state. Time + // to resign to be a regular ROP signed pointer + add x1, x0, #0x100 + autib lr, x1 + pacib lr, x16 // signed the scratch register for sp +#endif + ldp x0, x1, [x0, #0x000] // restore x0,x1 mov sp,x16 // restore sp #if defined(__ARM_FEATURE_GCS_DEFAULT) @@ -694,7 +705,12 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) gcspushm x30 Lnogcs: #endif + +#if __has_feature(ptrauth_calls) + retab +#else ret x30 // jump to pc +#endif #elif defined(__arm__) && !defined(__APPLE__) diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S index 5139a551ad245..74c5301674abc 100644 --- a/libunwind/src/UnwindRegistersSave.S +++ b/libunwind/src/UnwindRegistersSave.S @@ -767,6 +767,11 @@ LnoR2Fix: // .p2align 2 DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + +#if __has_feature(ptrauth_calls) + pacibsp +#endif + stp x0, x1, [x0, #0x000] stp x2, x3, [x0, #0x010] stp x4, x5, [x0, #0x020] @@ -807,7 +812,12 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) str d31, [x0, #0x208] #endif mov x0, #0 // return UNW_ESUCCESS + +#if __has_feature(ptrauth_calls) + retab +#else ret +#endif #elif defined(__arm__) && !defined(__APPLE__) diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp index cf39ec5f7dbdf..fc31566745143 100644 --- a/libunwind/src/libunwind.cpp +++ b/libunwind/src/libunwind.cpp @@ -16,6 +16,10 @@ #include +#if __has_include() +#include +#endif + // Define the __has_feature extension for compilers that do not support it so // that we can later check for the presence of ASan in a compiler-neutral way. #if !defined(__has_feature) @@ -126,6 +130,36 @@ _LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum, // First, get the FDE for the old location and then update it. co->getInfo(&info); co->setInfoBasedOnIPRegister(false); + +#if __has_feature(ptrauth_calls) + // It is only valid to set the IP within the current function. + // This is important for ptrauth, otherwise the IP cannot be correctly + // signed. + unw_word_t stripped_value = + (unw_word_t)ptrauth_strip((void *)value, ptrauth_key_return_address); + (void)stripped_value; + assert(stripped_value >= info.start_ip && stripped_value <= info.end_ip); +#endif + + pint_t sp = (pint_t)co->getReg(UNW_REG_SP); + +#if __has_feature(ptrauth_calls) + { + // PC should have been signed with the sp, so we verify that + // roundtripping does not fail. + pint_t pc = (pint_t)co->getReg(UNW_REG_IP); + if (ptrauth_auth_and_resign((void *)pc, ptrauth_key_return_address, sp, + ptrauth_key_return_address, + sp) != (void *)pc) { + _LIBUNWIND_LOG("Bad unwind through arm64e (0x%llX, 0x%llX)->0x%llX\n", + pc, sp, + (pint_t)ptrauth_auth_data( + (void *)pc, ptrauth_key_return_address, sp)); + _LIBUNWIND_ABORT("Bad unwind through arm64e"); + } + } +#endif + // If the original call expects stack adjustment, perform this now. // Normal frame unwinding would have included the offset already in the // CFA computation.