1 #include "exc_helpers.h"
2 #include <darwintest.h>
3
4 #include <mach/mach_init.h>
5 #include <mach/mach_port.h>
6 #include <pthread/private.h>
7 #include <pthread.h>
8 #include <sys/sysctl.h>
9 #include <mach/task.h>
10 #include <mach/thread_status.h>
11 #include <ptrauth.h>
12 #include <stdbool.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <mach/mach.h>
16 #include <mach/exception.h>
17 #include <mach/thread_status.h>
18 #include <sys/types.h>
19 #include <sys/sysctl.h>
20 #include <sys/code_signing.h>
21 #include <TargetConditionals.h>
22 #include <mach/semaphore.h>
23
24 #if __arm64__
25 #define EXCEPTION_THREAD_STATE ARM_THREAD_STATE64
26 #define EXCEPTION_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT
27 #elif __x86_64__
28 #define EXCEPTION_THREAD_STATE x86_THREAD_STATE
29 #define EXCEPTION_THREAD_STATE_COUNT x86_THREAD_STATE_COUNT
30 #else
31 #error Unsupported architecture
32 #endif
33
34 T_GLOBAL_META(
35 T_META_NAMESPACE("xnu.ipc"),
36 T_META_RADAR_COMPONENT_NAME("xnu"),
37 T_META_RADAR_COMPONENT_VERSION("IPC"),
38 T_META_RUN_CONCURRENTLY(true));
39
40 struct mach_exception_options {
41 mach_port_t exc_port;
42 exception_mask_t exceptions_allowed;
43 exception_behavior_t behaviors_allowed;
44 thread_state_flavor_t flavors_allowed;
45 };
46
47 #if __arm64__
48 static void
bad_access_func(void)49 bad_access_func(void)
50 {
51 T_QUIET; T_LOG("Crashing");
52 *(void *volatile *)5 = 0;
53 T_QUIET; T_LOG("Recoverd!");
54 return;
55 }
56 #endif /* __arm64__ */
57
58 static int num_exceptions = 0;
59
60 static uint32_t signing_key = (uint32_t)(0xa8000000 & 0xff000000);
61 static size_t
exc_handler_state_identity_protected(task_id_token_t token,uint64_t thread_id,exception_type_t type,__unused exception_data_t codes,thread_state_t in_state,mach_msg_type_number_t in_state_count,thread_state_t out_state,mach_msg_type_number_t * out_state_count)62 exc_handler_state_identity_protected(
63 task_id_token_t token,
64 uint64_t thread_id,
65 exception_type_t type,
66 __unused exception_data_t codes,
67 thread_state_t in_state,
68 mach_msg_type_number_t in_state_count,
69 thread_state_t out_state,
70 mach_msg_type_number_t *out_state_count)
71 {
72 mach_port_t port1, port2;
73 #pragma unused(port1)
74 #pragma unused(port2)
75 #pragma unused(token)
76 #pragma unused(thread_id)
77 #pragma unused(type)
78 #pragma unused(in_state)
79 #pragma unused(in_state_count)
80 #pragma unused(out_state)
81 #pragma unused(out_state_count)
82 *out_state_count = in_state_count;
83 T_LOG("Got protected exception!");
84 num_exceptions++;
85 #if __arm64__
86 arm_thread_state64_t *state = (arm_thread_state64_t*)(void *)out_state;
87 void *func_pc = (void *)arm_thread_state64_get_pc(*state);
88
89 /* Sign a PC which skips over the faulting instruction */
90 func_pc = ptrauth_strip(func_pc, ptrauth_key_function_pointer);
91 func_pc += 4;
92 uint64_t pc_discriminator = ptrauth_blend_discriminator((void *)(unsigned long)signing_key, ptrauth_string_discriminator("pc"));
93 func_pc = ptrauth_sign_unauthenticated(func_pc, ptrauth_key_function_pointer, pc_discriminator);
94
95 // Set/Sign the PC of the excepting thread
96 T_LOG("userspace discriminator=%llu\n", pc_discriminator);
97 arm_thread_state64_set_pc_presigned_fptr(*state, func_pc);
98
99 /* Corrupting the thread state should not crash as only the PC is accepted in hardened exceptions */
100 arm_thread_state64_set_lr_presigned_fptr(*state, func_pc);
101 arm_thread_state64_set_sp(*state, 0);
102 arm_thread_state64_set_fp(*state, 0);
103
104 #endif /* __arm64__ */
105
106 return KERN_SUCCESS;
107 }
108
109 static void
thread_register_handler(mach_port_t exc_port,const struct mach_exception_options meo)110 thread_register_handler(mach_port_t exc_port,
111 const struct mach_exception_options meo)
112 {
113 kern_return_t kr = thread_adopt_exception_handler(
114 mach_thread_self(), exc_port, meo.exceptions_allowed,
115 meo.behaviors_allowed, meo.flavors_allowed);
116
117 T_ASSERT_MACH_SUCCESS(kr, "thread register handler");
118 }
119
120 static mach_port_t
create_hardened_exception_port(const struct mach_exception_options meo,uint32_t signing_key_local)121 create_hardened_exception_port(const struct mach_exception_options meo,
122 uint32_t signing_key_local)
123 {
124 #if !__arm64__
125 T_SKIP("Hardened exceptions not supported on !arm64");
126 return MACH_PORT_NULL;
127 #else /* !__arm64__ */
128 kern_return_t kr;
129 mach_port_t exc_port;
130 mach_port_options_t opts = {
131 .flags = MPO_INSERT_SEND_RIGHT | MPO_EXCEPTION_PORT,
132 };
133
134 kr = mach_port_construct(mach_task_self(), &opts, 0ull, &exc_port);
135 T_ASSERT_MACH_SUCCESS(kr, "constructing mach port");
136
137 T_LOG("register with pc_signing_key=0x%x\n", signing_key_local);
138 kr = task_register_hardened_exception_handler(current_task(),
139 signing_key_local, meo.exceptions_allowed,
140 meo.behaviors_allowed, meo.flavors_allowed, exc_port);
141 T_ASSERT_MACH_SUCCESS(kr, "registering an exception handler port");
142 T_ASSERT_NE_UINT(exc_port, 0, "new exception port not null");
143
144 return exc_port;
145 #endif /* !__arm64__ */
146 }
147
148 T_DECL(hardened_exceptions_default,
149 "Test creating and using hardened exception ports") {
150 #if !__arm64__
151 T_SKIP("Hardened exceptions not supported on !arm64");
152 #else /* !__arm64__ */
153 struct mach_exception_options meo;
154 meo.exceptions_allowed = EXC_MASK_BAD_ACCESS;
155 meo.behaviors_allowed = EXCEPTION_STATE_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES;
156 meo.flavors_allowed = ARM_THREAD_STATE64;
157
158 mach_port_t exc_port = create_hardened_exception_port(meo, signing_key);
159
160 thread_register_handler(exc_port, meo);
161
162 run_exception_handler_behavior64(exc_port, NULL,
163 (void*)exc_handler_state_identity_protected,
164 EXCEPTION_STATE_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, true);
165 bad_access_func();
166
167 printf("Successfully recovered from the exception!\n");
168 #endif /* !__arm64__ */
169 }
170
171 extern char *__progname;
172
173 T_DECL(entitled_process_exceptions_disallowed,
174 "Test that when you have the special entitlement you may not use the hardened exception flow, unless you have debugger entitlements",
175 T_META_IGNORECRASHES("*hardened_exceptions_entitled")) {
176 #if !__arm64__
177 T_SKIP("Hardened exceptions not supported on !arm64");
178 #else /* !__arm64__ */
179
180 bool entitled = strstr(__progname, "entitled") != NULL;
181 bool debugger = strstr(__progname, "debugger") != NULL;
182 /* thread_set_exception_ports as a hardened binary should fail */
183 kern_return_t kr = thread_set_exception_ports(
184 mach_thread_self(),
185 EXC_MASK_ALL,
186 MACH_PORT_NULL,
187 (exception_behavior_t)((unsigned int)EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES),
188 EXCEPTION_THREAD_STATE);
189
190 if (!entitled && !debugger) {
191 T_ASSERT_MACH_SUCCESS(kr, "unentitled works normally");
192 } else if (entitled && !debugger) {
193 T_FAIL("We should have already crashed due to hardening entitlement");
194 } else if (entitled && debugger) {
195 T_ASSERT_MACH_SUCCESS(kr, "debugger entitlement works normally");
196 } else {
197 T_FAIL("invalid configuration");
198 }
199 #endif /* !__arm64__ */
200 }
201