summaryrefslogtreecommitdiff
path: root/libcxxabi
diff options
context:
space:
mode:
authorOliver Hunt <oliver@apple.com>2025-10-20 09:57:45 -0700
committerGitHub <noreply@github.com>2025-10-20 09:57:45 -0700
commite6a1aff5916447630e9ec0e8a90594ca1ee7a1be (patch)
tree58a0f725dfbb6af67cabaf14e7047d9963ca2bf3 /libcxxabi
parentcd05383a462672c1c6bab9bbef1bbbe1be616aa3 (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.h41
-rw-r--r--libcxxabi/src/cxa_exception.cpp4
-rw-r--r--libcxxabi/src/cxa_exception.h30
-rw-r--r--libcxxabi/src/cxa_personality.cpp129
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