diff options
| author | Oliver Hunt <oliver@apple.com> | 2025-10-20 09:57:45 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-20 09:57:45 -0700 |
| commit | e6a1aff5916447630e9ec0e8a90594ca1ee7a1be (patch) | |
| tree | 58a0f725dfbb6af67cabaf14e7047d9963ca2bf3 /libcxxabi | |
| parent | cd05383a462672c1c6bab9bbef1bbbe1be616aa3 (diff) | |
[runtimes][PAC] Harden unwinding when possible (#143230)
This hardens the unwinding logic and datastructures on systems
that support pointer authentication.
The approach taken to hardening is to harden the schemas of as many
high value fields in the myriad structs as possible, and then also
explicitly qualify local variables referencing privileged or security
critical values.
This does introduce ABI linkage between libcxx, libcxxabi, and
libunwind but those are in principle separate from the OS itself
so we've kept the schema definitions in the library specific headers
rather than ptrauth.h
Diffstat (limited to 'libcxxabi')
| -rw-r--r-- | libcxxabi/include/__cxxabi_config.h | 41 | ||||
| -rw-r--r-- | libcxxabi/src/cxa_exception.cpp | 4 | ||||
| -rw-r--r-- | libcxxabi/src/cxa_exception.h | 30 | ||||
| -rw-r--r-- | libcxxabi/src/cxa_personality.cpp | 129 |
4 files changed, 180 insertions, 24 deletions
diff --git a/libcxxabi/include/__cxxabi_config.h b/libcxxabi/include/__cxxabi_config.h index 759445dac91f..f5101dbc9e59 100644 --- a/libcxxabi/include/__cxxabi_config.h +++ b/libcxxabi/include/__cxxabi_config.h @@ -103,6 +103,47 @@ #define _LIBCXXABI_DTOR_FUNC #endif +#if __has_include(<ptrauth.h>) +# include <ptrauth.h> +#endif + +#if __has_feature(ptrauth_calls) + +// ptrauth_string_discriminator("__cxa_exception::actionRecord") == 0xFC91 +# define __ptrauth_cxxabi_action_record __ptrauth(ptrauth_key_process_dependent_data, 1, 0xFC91) + +// ptrauth_string_discriminator("__cxa_exception::languageSpecificData") == 0xE8EE +# define __ptrauth_cxxabi_lsd __ptrauth(ptrauth_key_process_dependent_data, 1, 0xE8EE) + +// ptrauth_string_discriminator("__cxa_exception::catchTemp") == 0xFA58 +# define __ptrauth_cxxabi_catch_temp_disc 0xFA58 +# define __ptrauth_cxxabi_catch_temp_key ptrauth_key_process_dependent_data +# define __ptrauth_cxxabi_catch_temp __ptrauth(__ptrauth_cxxabi_catch_temp_key, 1, __ptrauth_cxxabi_catch_temp_disc) + +// ptrauth_string_discriminator("__cxa_exception::adjustedPtr") == 0x99E4 +# define __ptrauth_cxxabi_adjusted_ptr __ptrauth(ptrauth_key_process_dependent_data, 1, 0x99E4) + +// ptrauth_string_discriminator("__cxa_exception::unexpectedHandler") == 0x99A9 +# define __ptrauth_cxxabi_unexpected_handler __ptrauth(ptrauth_key_function_pointer, 1, 0x99A9) + +// ptrauth_string_discriminator("__cxa_exception::terminateHandler") == 0x0886) +# define __ptrauth_cxxabi_terminate_handler __ptrauth(ptrauth_key_function_pointer, 1, 0x886) + +// ptrauth_string_discriminator("__cxa_exception::exceptionDestructor") == 0xC088 +# define __ptrauth_cxxabi_exception_destructor __ptrauth(ptrauth_key_function_pointer, 1, 0xC088) + +#else + +# define __ptrauth_cxxabi_action_record +# define __ptrauth_cxxabi_lsd +# define __ptrauth_cxxabi_catch_temp +# define __ptrauth_cxxabi_adjusted_ptr +# define __ptrauth_cxxabi_unexpected_handler +# define __ptrauth_cxxabi_terminate_handler +# define __ptrauth_cxxabi_exception_destructor + +#endif + #if __cplusplus < 201103L # define _LIBCXXABI_NOEXCEPT throw() #else diff --git a/libcxxabi/src/cxa_exception.cpp b/libcxxabi/src/cxa_exception.cpp index 92901a83bfd0..73ba21c1df7c 100644 --- a/libcxxabi/src/cxa_exception.cpp +++ b/libcxxabi/src/cxa_exception.cpp @@ -192,7 +192,9 @@ void *__cxa_allocate_exception(size_t thrown_size) throw() { std::terminate(); __cxa_exception *exception_header = static_cast<__cxa_exception *>((void *)(raw_buffer + header_offset)); - ::memset(exception_header, 0, actual_size); + // We warn on memset to a non-trivially castable type. We might want to + // change that diagnostic to not fire on a trivially obvious zero fill. + ::memset(static_cast<void*>(exception_header), 0, actual_size); return thrown_object_from_cxa_exception(exception_header); } diff --git a/libcxxabi/src/cxa_exception.h b/libcxxabi/src/cxa_exception.h index aba08f299210..fa4c4dc55bde 100644 --- a/libcxxabi/src/cxa_exception.h +++ b/libcxxabi/src/cxa_exception.h @@ -47,10 +47,10 @@ 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 *__ptrauth_cxxabi_exception_destructor exceptionDestructor)(void *); #endif - std::unexpected_handler unexpectedHandler; - std::terminate_handler terminateHandler; + std::unexpected_handler __ptrauth_cxxabi_unexpected_handler unexpectedHandler; + std::terminate_handler __ptrauth_cxxabi_terminate_handler terminateHandler; __cxa_exception *nextException; @@ -61,10 +61,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 *__ptrauth_cxxabi_action_record actionRecord; + const unsigned char *__ptrauth_cxxabi_lsd languageSpecificData; + void *__ptrauth_cxxabi_catch_temp catchTemp; + void *__ptrauth_cxxabi_adjusted_ptr adjustedPtr; #endif #if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI) @@ -79,6 +79,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 pointer authentication schemas specified here must also 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 +88,9 @@ 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 *__ptrauth_cxxabi_exception_destructor exceptionDestructor)(void *); + std::unexpected_handler __ptrauth_cxxabi_unexpected_handler unexpectedHandler; + std::terminate_handler __ptrauth_cxxabi_terminate_handler terminateHandler; __cxa_exception *nextException; @@ -99,10 +101,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 *__ptrauth_cxxabi_action_record actionRecord; + const unsigned char *__ptrauth_cxxabi_lsd languageSpecificData; + void *__ptrauth_cxxabi_catch_temp catchTemp; + void *__ptrauth_cxxabi_adjusted_ptr 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 5f6e75c5be19..b7eb0f23dbe0 100644 --- a/libcxxabi/src/cxa_personality.cpp +++ b/libcxxabi/src/cxa_personality.cpp @@ -20,7 +20,47 @@ #include "cxa_exception.h" #include "cxa_handlers.h" #include "private_typeinfo.h" -#include "unwind.h" + +#if __has_feature(ptrauth_calls) + +// CXXABI depends on defintions in libunwind as pointer auth couples the +// definitions +# include "libunwind.h" + +// The actual value of the discriminators listed below is not important. +// The derivation of the constants is only being included for the purpose +// of maintaining a record of how they were originally produced. + +// ptrauth_string_discriminator("scan_results::languageSpecificData") == 0xE50D) +# define __ptrauth_scan_results_lsd __ptrauth(ptrauth_key_process_dependent_code, 1, 0xE50D) + +// ptrauth_string_discriminator("scan_results::actionRecord") == 0x9823 +# define __ptrauth_scan_results_action_record __ptrauth(ptrauth_key_process_dependent_code, 1, 0x9823) + +// scan result is broken up as we have a manual re-sign that requires each component +# define __ptrauth_scan_results_landingpad_key ptrauth_key_process_dependent_code +// ptrauth_string_discriminator("scan_results::landingPad") == 0xD27C +# define __ptrauth_scan_results_landingpad_disc 0xD27C +# define __ptrauth_scan_results_landingpad \ + __ptrauth(__ptrauth_scan_results_landingpad_key, 1, __ptrauth_scan_results_landingpad_disc) + +// `__ptrauth_restricted_intptr` is a feature of apple clang that predates +// support for direct application of `__ptrauth` to integer types. This +// guard is necessary to support compilation with those compiler. +# if __has_extension(ptrauth_restricted_intptr_qualifier) +# define __ptrauth_scan_results_landingpad_intptr \ + __ptrauth_restricted_intptr(__ptrauth_scan_results_landingpad_key, 1, __ptrauth_scan_results_landingpad_disc) +# else +# define __ptrauth_scan_results_landingpad_intptr \ + __ptrauth(__ptrauth_scan_results_landingpad_key, 1, __ptrauth_scan_results_landingpad_disc) +# endif + +#else +# define __ptrauth_scan_results_lsd +# define __ptrauth_scan_results_action_record +# define __ptrauth_scan_results_landingpad +# define __ptrauth_scan_results_landingpad_intptr +#endif // 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 @@ -527,12 +567,17 @@ get_thrown_object_ptr(_Unwind_Exception* unwind_exception) namespace { +typedef const uint8_t *__ptrauth_scan_results_lsd lsd_ptr_t; +typedef const uint8_t *__ptrauth_scan_results_action_record action_ptr_t; +typedef uintptr_t __ptrauth_scan_results_landingpad_intptr landing_pad_t; +typedef void *__ptrauth_scan_results_landingpad 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, @@ -557,7 +602,23 @@ set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context, reinterpret_cast<uintptr_t>(unwind_exception)); _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), static_cast<uintptr_t>(results.ttypeIndex)); +#if __has_feature(ptrauth_calls) + auto stackPointer = _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_scan_results_landingpad_disc); + unw_word_t newIP /* opaque __ptrauth(ptrauth_key_return_address, stackPointer, 0) */ = + (unw_word_t)ptrauth_auth_and_resign(*(void* const*)&results.landingPad, + __ptrauth_scan_results_landingpad_key, + existingDiscriminator, + ptrauth_key_return_address, + stackPointer); + _Unwind_SetIP(context, newIP); +#else _Unwind_SetIP(context, results.landingPad); +#endif } /* @@ -691,12 +752,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__ @@ -903,6 +964,57 @@ _UA_CLEANUP_PHASE */ #if !defined(_LIBCXXABI_ARM_EHABI) + +// We use these helper functions to work around the behavior of casting between +// integers (even those that are authenticated) and authenticated pointers. +// Because the schemas being used are address discriminated we cannot use a +// trivial value union to coerce the types so instead we perform the re-signing +// manually. +using __cxa_catch_temp_type = decltype(__cxa_exception::catchTemp); +static inline void set_landing_pad(scan_results& results, + const __cxa_catch_temp_type& source) { +#if __has_feature(ptrauth_calls) + const uintptr_t sourceDiscriminator = + ptrauth_blend_discriminator(&source, __ptrauth_cxxabi_catch_temp_disc); + const uintptr_t targetDiscriminator = + ptrauth_blend_discriminator(&results.landingPad, + __ptrauth_scan_results_landingpad_disc); + uintptr_t reauthenticatedLandingPad = + (uintptr_t)ptrauth_auth_and_resign(*reinterpret_cast<void* const*>(&source), + __ptrauth_cxxabi_catch_temp_key, + sourceDiscriminator, + __ptrauth_scan_results_landingpad_key, + targetDiscriminator); + memmove(reinterpret_cast<void *>(&results.landingPad), + reinterpret_cast<void *>(&reauthenticatedLandingPad), + sizeof(reauthenticatedLandingPad)); +#else + results.landingPad = reinterpret_cast<landing_pad_t>(source); +#endif +} + +static inline void get_landing_pad(__cxa_catch_temp_type &dest, + const scan_results &results) { +#if __has_feature(ptrauth_calls) + const uintptr_t sourceDiscriminator = + ptrauth_blend_discriminator(&results.landingPad, + __ptrauth_scan_results_landingpad_disc); + const uintptr_t targetDiscriminator = + ptrauth_blend_discriminator(&dest, __ptrauth_cxxabi_catch_temp_disc); + uintptr_t reauthenticatedPointer = + (uintptr_t)ptrauth_auth_and_resign(*reinterpret_cast<void* const*>(&results.landingPad), + __ptrauth_scan_results_landingpad_key, + sourceDiscriminator, + __ptrauth_cxxabi_catch_temp_key, + targetDiscriminator); + memmove(reinterpret_cast<void *>(&dest), + reinterpret_cast<void *>(&reauthenticatedPointer), + sizeof(reauthenticatedPointer)); +#else + dest = reinterpret_cast<__cxa_catch_temp_type>(results.landingPad); +#endif +} + #ifdef __WASM_EXCEPTIONS__ _Unwind_Reason_Code __gxx_personality_wasm0 #elif defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) @@ -935,8 +1047,7 @@ __gxx_personality_v0 results.ttypeIndex = exception_header->handlerSwitchValue; results.actionRecord = exception_header->actionRecord; results.languageSpecificData = exception_header->languageSpecificData; - results.landingPad = - reinterpret_cast<uintptr_t>(exception_header->catchTemp); + set_landing_pad(results, exception_header->catchTemp); results.adjustedPtr = exception_header->adjustedPtr; // Jump to the handler. @@ -970,7 +1081,7 @@ __gxx_personality_v0 exc->handlerSwitchValue = static_cast<int>(results.ttypeIndex); exc->actionRecord = results.actionRecord; exc->languageSpecificData = results.languageSpecificData; - exc->catchTemp = reinterpret_cast<void*>(results.landingPad); + get_landing_pad(exc->catchTemp, results); exc->adjustedPtr = results.adjustedPtr; #ifdef __WASM_EXCEPTIONS__ // Wasm only uses a single phase (_UA_SEARCH_PHASE), so save the |
