Skip to content

Commit 9566f3d

Browse files
committed
[runtimes][PAC] Harden unwinding when possible (#138571)
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
1 parent 7368558 commit 9566f3d

15 files changed

+693
-100
lines changed

compiler-rt/lib/builtins/gcc_personality_v0.c

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,45 @@ EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *, PCONTEXT,
3030
_Unwind_Personality_Fn);
3131
#endif
3232

33+
#if __has_feature(ptrauth_qualifier)
34+
#include <ptrauth.h>
35+
#if __has_feature(ptrauth_restricted_intptr_qualifier)
36+
#define __ptrauth_gcc_personality_intptr(key, addressDiscriminated, \
37+
discriminator) \
38+
__ptrauth_restricted_intptr(key, addressDiscriminated, discriminator)
39+
#else
40+
#define __ptrauth_gcc_personality_intptr(key, addressDiscriminated, \
41+
discriminator) \
42+
__ptrauth(key, addressDiscriminated, discriminator)
43+
#endif
44+
#else
45+
#define __ptrauth_gcc_personality_intptr(...)
46+
#endif
47+
48+
#define __ptrauth_gcc_personality_func_key ptrauth_key_function_pointer
49+
50+
// ptrauth_string_discriminator("__gcc_personality_v0'funcStart") == 0xDFEB
51+
#define __ptrauth_gcc_personality_func_start \
52+
__ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, 0xDFEB)
53+
54+
// ptrauth_string_discriminator("__gcc_personality_v0'start") == 0x52DC
55+
#define __ptrauth_gcc_personality_start \
56+
__ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, 0x52DC)
57+
58+
// ptrauth_string_discriminator("__gcc_personality_v0'length") == 0xFFF7
59+
#define __ptrauth_gcc_personality_length \
60+
__ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, 0xFFF7)
61+
62+
// ptrauth_string_discriminator("__gcc_personality_v0'landingPadOffset") == 0x6498
63+
#define __ptrauth_gcc_personality_lpoffset \
64+
__ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, 0x6498)
65+
66+
// ptrauth_string_discriminator("__gcc_personality_v0'landingPad") == 0xA134
67+
#define __ptrauth_gcc_personality_lpad_disc 0xA134
68+
#define __ptrauth_gcc_personality_lpad \
69+
__ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, \
70+
__ptrauth_gcc_personality_lpad_disc)
71+
3372
// Pointer encodings documented at:
3473
// http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
3574

@@ -205,7 +244,8 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
205244
return continueUnwind(exceptionObject, context);
206245

207246
uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1;
208-
uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
247+
uintptr_t __ptrauth_gcc_personality_func_start funcStart =
248+
(uintptr_t)_Unwind_GetRegionStart(context);
209249
uintptr_t pcOffset = pc - funcStart;
210250

211251
// Parse LSDA header.
@@ -224,11 +264,14 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
224264
const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;
225265
const uint8_t *p = callSiteTableStart;
226266
while (p < callSiteTableEnd) {
227-
uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
228-
size_t length = readEncodedPointer(&p, callSiteEncoding);
229-
size_t landingPad = readEncodedPointer(&p, callSiteEncoding);
267+
uintptr_t __ptrauth_gcc_personality_start start =
268+
readEncodedPointer(&p, callSiteEncoding);
269+
size_t __ptrauth_gcc_personality_length length =
270+
readEncodedPointer(&p, callSiteEncoding);
271+
size_t __ptrauth_gcc_personality_lpoffset landingPadOffset =
272+
readEncodedPointer(&p, callSiteEncoding);
230273
readULEB128(&p); // action value not used for C code
231-
if (landingPad == 0)
274+
if (landingPadOffset == 0)
232275
continue; // no landing pad for this entry
233276
if ((start <= pcOffset) && (pcOffset < (start + length))) {
234277
// Found landing pad for the PC.
@@ -238,7 +281,28 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
238281
_Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
239282
(uintptr_t)exceptionObject);
240283
_Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
241-
_Unwind_SetIP(context, (funcStart + landingPad));
284+
size_t __ptrauth_gcc_personality_lpad landingPad =
285+
funcStart + landingPadOffset;
286+
#if __has_feature(ptrauth_qualifier)
287+
uintptr_t stackPointer = _Unwind_GetGR(context, -2);
288+
const uintptr_t existingDiscriminator =
289+
ptrauth_blend_discriminator(&landingPad,
290+
__ptrauth_gcc_personality_lpad_disc);
291+
// newIP is authenticated as if it were qualified with a pseudo qualifier
292+
// along the lines of:
293+
// __ptrauth(ptrauth_key_return_address, <stackPointer>, 0)
294+
// where the stack pointer is used in place of the strict storage
295+
// address.
296+
uintptr_t newIP =
297+
(uintptr_t)ptrauth_auth_and_resign(*(void **)&landingPad,
298+
__ptrauth_gcc_personality_func_key,
299+
existingDiscriminator,
300+
ptrauth_key_return_address,
301+
stackPointer);
302+
_Unwind_SetIP(context, newIP);
303+
#else
304+
_Unwind_SetIP(context, landingPad);
305+
#endif
242306
return _URC_INSTALL_CONTEXT;
243307
}
244308
}

libcxxabi/include/__cxxabi_config.h

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#define _LIBCXXABI_FUNC_VIS __declspec(dllimport)
4949
#define _LIBCXXABI_TYPE_VIS __declspec(dllimport)
5050
#endif
51+
5152
#else
5253
#if !defined(_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS)
5354
#define _LIBCXXABI_HIDDEN __attribute__((__visibility__("hidden")))
@@ -103,6 +104,52 @@
103104
#define _LIBCXXABI_DTOR_FUNC
104105
#endif
105106

107+
#if __has_include(<ptrauth.h>)
108+
# include <ptrauth.h>
109+
#endif
110+
111+
#if __has_extension(ptrauth_qualifier)
112+
113+
// ptrauth_string_discriminator("__cxa_exception::actionRecord") == 0xFC91
114+
# define __ptrauth_cxxabi_action_record \
115+
__ptrauth(ptrauth_key_process_dependent_data, 1, 0xFC91)
116+
117+
// ptrauth_string_discriminator("__cxa_exception::languageSpecificData") == 0xE8EE
118+
# define __ptrauth_cxxabi_lsd \
119+
__ptrauth(ptrauth_key_process_dependent_data, 1, 0xE8EE)
120+
121+
// ptrauth_string_discriminator("__cxa_exception::catchTemp") == 0xFA58
122+
# define __ptrauth_cxxabi_catch_temp \
123+
__ptrauth(ptrauth_key_process_dependent_data, 1, 0xFA58)
124+
125+
// ptrauth_string_discriminator("__cxa_exception::adjustedPtr") == 0x99E4
126+
# define __ptrauth_cxxabi_adjusted_ptr \
127+
__ptrauth(ptrauth_key_process_dependent_data, 1, 0x99E4)
128+
129+
// ptrauth_string_discriminator("__cxa_exception::unexpectedHandler") == 0x99A9
130+
# define __ptrauth_cxxabi_unexpected_handler \
131+
__ptrauth(ptrauth_key_function_pointer, 1, 0x99A9)
132+
133+
// ptrauth_string_discriminator("__cxa_exception::terminateHandler") == 0x0886)
134+
# define __ptrauth_cxxabi_terminate_handler \
135+
__ptrauth(ptrauth_key_function_pointer, 1, 0x886)
136+
137+
// ptrauth_string_discriminator("__cxa_exception::exceptionDestructor") == 0xC088
138+
# define __ptrauth_cxxabi_exception_destructor \
139+
__ptrauth(ptrauth_key_function_pointer, 1, 0xC088)
140+
141+
#else
142+
143+
# define __ptrauth_cxxabi_action_record
144+
# define __ptrauth_cxxabi_lsd
145+
# define __ptrauth_cxxabi_catch_temp
146+
# define __ptrauth_cxxabi_adjusted_ptr
147+
# define __ptrauth_cxxabi_unexpected_handler
148+
# define __ptrauth_cxxabi_terminate_handler
149+
# define __ptrauth_cxxabi_exception_destructor
150+
151+
#endif
152+
106153
#if __cplusplus < 201103L
107154
# define _LIBCXXABI_NOEXCEPT throw()
108155
#else

libcxxabi/src/cxa_exception.h

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@ struct _LIBCXXABI_HIDDEN __cxa_exception {
4747
// In Wasm, a destructor returns its argument
4848
void *(_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *);
4949
#else
50-
void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *);
50+
void(_LIBCXXABI_DTOR_FUNC* __ptrauth_cxxabi_exception_destructor
51+
exceptionDestructor)(void*);
5152
#endif
52-
std::unexpected_handler unexpectedHandler;
53-
std::terminate_handler terminateHandler;
53+
std::unexpected_handler __ptrauth_cxxabi_unexpected_handler unexpectedHandler;
54+
std::terminate_handler __ptrauth_cxxabi_terminate_handler terminateHandler;
5455

5556
__cxa_exception *nextException;
5657

@@ -61,10 +62,10 @@ struct _LIBCXXABI_HIDDEN __cxa_exception {
6162
int propagationCount;
6263
#else
6364
int handlerSwitchValue;
64-
const unsigned char *actionRecord;
65-
const unsigned char *languageSpecificData;
66-
void *catchTemp;
67-
void *adjustedPtr;
65+
const unsigned char* __ptrauth_cxxabi_action_record actionRecord;
66+
const unsigned char* __ptrauth_cxxabi_lsd languageSpecificData;
67+
void* __ptrauth_cxxabi_catch_temp catchTemp;
68+
void* __ptrauth_cxxabi_adjusted_ptr adjustedPtr;
6869
#endif
6970

7071
#if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI)
@@ -79,16 +80,19 @@ struct _LIBCXXABI_HIDDEN __cxa_exception {
7980
// http://sourcery.mentor.com/archives/cxx-abi-dev/msg01924.html
8081
// The layout of this structure MUST match the layout of __cxa_exception, with
8182
// primaryException instead of referenceCount.
83+
// The tags used in the pointer authentication qualifiers also need to match
84+
// those of the corresponding members in __cxa_exception.
8285
struct _LIBCXXABI_HIDDEN __cxa_dependent_exception {
8386
#if defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI)
8487
void* reserve; // padding.
8588
void* primaryException;
8689
#endif
8790

8891
std::type_info *exceptionType;
89-
void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *);
90-
std::unexpected_handler unexpectedHandler;
91-
std::terminate_handler terminateHandler;
92+
void(_LIBCXXABI_DTOR_FUNC* __ptrauth_cxxabi_exception_destructor
93+
exceptionDestructor)(void*);
94+
std::unexpected_handler __ptrauth_cxxabi_unexpected_handler unexpectedHandler;
95+
std::terminate_handler __ptrauth_cxxabi_terminate_handler terminateHandler;
9296

9397
__cxa_exception *nextException;
9498

@@ -99,10 +103,11 @@ struct _LIBCXXABI_HIDDEN __cxa_dependent_exception {
99103
int propagationCount;
100104
#else
101105
int handlerSwitchValue;
102-
const unsigned char *actionRecord;
103-
const unsigned char *languageSpecificData;
104-
void * catchTemp;
105-
void *adjustedPtr;
106+
107+
const unsigned char* __ptrauth_cxxabi_action_record actionRecord;
108+
const unsigned char* __ptrauth_cxxabi_lsd languageSpecificData;
109+
void* __ptrauth_cxxabi_catch_temp catchTemp;
110+
void* __ptrauth_cxxabi_adjusted_ptr adjustedPtr;
106111
#endif
107112

108113
#if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI)

libcxxabi/src/cxa_personality.cpp

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,51 @@
2121
#include "cxa_handlers.h"
2222
#include "private_typeinfo.h"
2323
#include "unwind.h"
24+
#include "libunwind.h"
25+
26+
#if __has_include(<ptrauth.h>)
27+
# include <ptrauth.h>
28+
#endif
29+
30+
#if __has_extension(ptrauth_qualifier)
31+
// The actual value of the discriminators listed below is not important.
32+
// The derivation of the constants is only being included for the purpose
33+
// of maintaining a record of how they were originally produced.
34+
35+
// ptrauth_string_discriminator("scan_results::languageSpecificData") == 0xE50D)
36+
#define __ptrauth_scan_results_lsd \
37+
__ptrauth(ptrauth_key_process_dependent_code, 1, 0xE50D)
38+
39+
// ptrauth_string_discriminator("scan_results::actionRecord") == 0x9823
40+
#define __ptrauth_scan_results_action_record \
41+
__ptrauth(ptrauth_key_process_dependent_code, 1, 0x9823)
42+
43+
// scan result is broken up as we have a manual re-sign that requires each component
44+
#define __ptrauth_scan_results_landingpad_key ptrauth_key_process_dependent_code
45+
// ptrauth_string_discriminator("scan_results::landingPad") == 0xD27C
46+
#define __ptrauth_scan_results_landingpad_disc 0xD27C
47+
#define __ptrauth_scan_results_landingpad \
48+
__ptrauth(__ptrauth_scan_results_landingpad_key, 1, __ptrauth_scan_results_landingpad_disc)
49+
50+
#if __has_extension(__ptrauth_restricted_intptr)
51+
#define __ptrauth_scan_results_landingpad_intptr \
52+
__ptrauth_restricted_intptr(__ptrauth_scan_results_landingpad_key, 1, \
53+
__ptrauth_scan_results_landingpad_disc)
54+
#else
55+
#define __ptrauth_scan_results_landingpad_intptr \
56+
__ptrauth(__ptrauth_scan_results_landingpad_key, 1, \
57+
__ptrauth_scan_results_landingpad_disc)
58+
#endif
59+
60+
#else
61+
#define __ptrauth_scan_results_lsd
62+
#define __ptrauth_scan_results_action_record
63+
#define __ptrauth_scan_results_landingpad
64+
#define __ptrauth_scan_results_landingpad_intptr
65+
#endif
66+
67+
68+
#include "libunwind.h"
2469

2570
// TODO: This is a temporary workaround for libc++abi to recognize that it's being
2671
// built against LLVM's libunwind. LLVM's libunwind started reporting _LIBUNWIND_VERSION
@@ -527,12 +572,17 @@ get_thrown_object_ptr(_Unwind_Exception* unwind_exception)
527572
namespace
528573
{
529574

575+
typedef const uint8_t* __ptrauth_scan_results_lsd lsd_ptr_t;
576+
typedef const uint8_t* __ptrauth_scan_results_action_record action_ptr_t;
577+
typedef uintptr_t __ptrauth_scan_results_landingpad_intptr landing_pad_t;
578+
typedef void* __ptrauth_scan_results_landingpad landing_pad_ptr_t;
579+
530580
struct scan_results
531581
{
532582
int64_t ttypeIndex; // > 0 catch handler, < 0 exception spec handler, == 0 a cleanup
533-
const uint8_t* actionRecord; // Currently unused. Retained to ease future maintenance.
534-
const uint8_t* languageSpecificData; // Needed only for __cxa_call_unexpected
535-
uintptr_t landingPad; // null -> nothing found, else something found
583+
action_ptr_t actionRecord; // Currently unused. Retained to ease future maintenance.
584+
lsd_ptr_t languageSpecificData; // Needed only for __cxa_call_unexpected
585+
landing_pad_t landingPad; // null -> nothing found, else something found
536586
void* adjustedPtr; // Used in cxa_exception.cpp
537587
_Unwind_Reason_Code reason; // One of _URC_FATAL_PHASE1_ERROR,
538588
// _URC_FATAL_PHASE2_ERROR,
@@ -541,7 +591,33 @@ struct scan_results
541591
};
542592

543593
} // unnamed namespace
594+
}
595+
596+
namespace {
597+
// The logical model for casting authenticated function pointers makes
598+
// it impossible to directly cast them without breaking the authentication,
599+
// as a result we need this pair of helpers.
600+
template <typename PtrType>
601+
void set_landing_pad_as_ptr(scan_results& results, const PtrType& out) {
602+
union {
603+
landing_pad_t* as_landing_pad;
604+
landing_pad_ptr_t* as_pointer;
605+
} u;
606+
u.as_landing_pad = &results.landingPad;
607+
*u.as_pointer = out;
608+
}
544609

610+
static const landing_pad_ptr_t& get_landing_pad_as_ptr(const scan_results& results) {
611+
union {
612+
const landing_pad_t* as_landing_pad;
613+
const landing_pad_ptr_t* as_pointer;
614+
} u;
615+
u.as_landing_pad = &results.landingPad;
616+
return *u.as_pointer;
617+
}
618+
} // unnamed namespace
619+
620+
extern "C" {
545621
static
546622
void
547623
set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context,
@@ -557,7 +633,21 @@ set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context,
557633
reinterpret_cast<uintptr_t>(unwind_exception));
558634
_Unwind_SetGR(context, __builtin_eh_return_data_regno(1),
559635
static_cast<uintptr_t>(results.ttypeIndex));
636+
#if __has_feature(ptrauth_qualifier)
637+
auto stack_pointer = _Unwind_GetGR(context, UNW_REG_SP);
638+
// We manually re-sign the IP as the __ptrauth qualifiers cannot
639+
// express the required relationship with the destination address
640+
const auto existingDiscriminator = ptrauth_blend_discriminator(
641+
&results.landingPad, __ptrauth_scan_results_landingpad_disc);
642+
unw_word_t newIP /* opaque __ptrauth(ptrauth_key_return_address, stack_pointer, 0) */ =
643+
(unw_word_t)ptrauth_auth_and_resign(*(void**)&results.landingPad,
644+
__ptrauth_scan_results_landingpad_key,
645+
existingDiscriminator,
646+
ptrauth_key_return_address, stack_pointer);
647+
_Unwind_SetIP(context, newIP);
648+
#else
560649
_Unwind_SetIP(context, results.landingPad);
650+
#endif
561651
}
562652

563653
/*
@@ -691,12 +781,12 @@ static void scan_eh_tab(scan_results &results, _Unwind_Action actions,
691781
// The call sites are ordered in increasing value of start
692782
uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding);
693783
uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding);
694-
uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding);
784+
landing_pad_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding);
695785
uintptr_t actionEntry = readULEB128(&callSitePtr);
696786
if ((start <= ipOffset) && (ipOffset < (start + length)))
697787
#else // __USING_SJLJ_EXCEPTIONS__ || __WASM_EXCEPTIONS__
698788
// ip is 1-based index into this table
699-
uintptr_t landingPad = readULEB128(&callSitePtr);
789+
landing_pad_t landingPad = readULEB128(&callSitePtr);
700790
uintptr_t actionEntry = readULEB128(&callSitePtr);
701791
if (--ip == 0)
702792
#endif // __USING_SJLJ_EXCEPTIONS__ || __WASM_EXCEPTIONS__
@@ -935,8 +1025,7 @@ __gxx_personality_v0
9351025
results.ttypeIndex = exception_header->handlerSwitchValue;
9361026
results.actionRecord = exception_header->actionRecord;
9371027
results.languageSpecificData = exception_header->languageSpecificData;
938-
results.landingPad =
939-
reinterpret_cast<uintptr_t>(exception_header->catchTemp);
1028+
set_landing_pad_as_ptr(results, exception_header->catchTemp);
9401029
results.adjustedPtr = exception_header->adjustedPtr;
9411030

9421031
// Jump to the handler.
@@ -970,7 +1059,7 @@ __gxx_personality_v0
9701059
exc->handlerSwitchValue = static_cast<int>(results.ttypeIndex);
9711060
exc->actionRecord = results.actionRecord;
9721061
exc->languageSpecificData = results.languageSpecificData;
973-
exc->catchTemp = reinterpret_cast<void*>(results.landingPad);
1062+
exc->catchTemp = get_landing_pad_as_ptr(results);
9741063
exc->adjustedPtr = results.adjustedPtr;
9751064
#ifdef __WASM_EXCEPTIONS__
9761065
// Wasm only uses a single phase (_UA_SEARCH_PHASE), so save the

0 commit comments

Comments
 (0)