]>
Commit | Line | Data |
---|---|---|
0a7de745 A |
1 | /* |
2 | * Copyright (c) 2019 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | // #define STANDALONE | |
30 | ||
31 | #ifndef STANDALONE | |
32 | #include <darwintest.h> | |
33 | #endif | |
34 | #include <architecture/i386/table.h> | |
35 | #include <i386/user_ldt.h> | |
36 | #include <mach/i386/vm_param.h> | |
37 | #include <mach/i386/thread_status.h> | |
38 | #include <mach/mach.h> | |
39 | #include <signal.h> | |
40 | #include <stdio.h> | |
41 | #include <stdlib.h> | |
42 | #include <strings.h> | |
43 | #include <sys/mman.h> | |
44 | #include <sys/types.h> | |
45 | #include <sys/signal.h> | |
46 | #include <sys/sysctl.h> | |
47 | #include <assert.h> | |
48 | #include <errno.h> | |
49 | #include <fcntl.h> | |
50 | #include <pthread.h> | |
51 | #include <unistd.h> | |
52 | #include <ldt_mach_exc.h> | |
53 | ||
54 | #ifndef STANDALONE | |
55 | T_GLOBAL_META( | |
56 | T_META_NAMESPACE("xnu.intel"), | |
57 | T_META_CHECK_LEAKS(false) | |
58 | ); | |
59 | #endif | |
60 | ||
61 | #define COMPAT_MODE_CS_SELECTOR 0x1f | |
62 | #define SYSENTER_SELECTOR 0xb | |
63 | /* #define DEBUG 1 */ | |
64 | #define P2ROUNDUP(x, align) (-(-((long)x) & -((long)align))) | |
65 | #define MSG 2048 | |
66 | ||
67 | #define NORMAL_RUN_TIME (10) | |
68 | #define TIMEOUT_OVERHEAD (10) | |
69 | ||
70 | /* | |
71 | * General theory of operation: | |
72 | * ---------------------------- | |
73 | * (1) Ensure that all code and data to be accessed from compatibility mode is | |
74 | * located in the low 4GiB of virtual address space. | |
75 | * (2) Allocate required segments via the i386_set_ldt() system call, making | |
76 | * sure to set the descriptor type correctly (code vs. data). Creating | |
77 | * 64-bit code segments is not allowed (just use the existing 0x2b selector.) | |
78 | * (3) Once you know which selector is associated with the desired code, use a | |
79 | * trampoline (or thunk) to (a) switch to a stack that's located below 4GiB | |
80 | * and (b) save ABI-mandated caller-saved state so that if it's trashed by | |
81 | * compatibility-mode code, it can be restored before returning to 64-bit | |
82 | * mode (if desired), and finally (c) long-jump or long-call (aka far call) | |
83 | * to the segment and desired offset (this example uses an offset of 0 for | |
84 | * simplicity.) | |
85 | * (4) Once in compatibility mode, if a framework call or system call is required, | |
86 | * the code must trampoline back to 64-bit mode to do so. System calls from | |
87 | * compatibility mode code are not supported and will result in invalid opcode | |
88 | * exceptions. This example includes a simple 64-bit trampoline (which must | |
89 | * be located in the low 4GiB of virtual address space, since it's executed | |
90 | * by compatibility-mode code.) Note that since the 64-bit ABI mandates that | |
91 | * the stack must be aligned to a 16-byte boundary, the sample trampoline | |
92 | * performs that rounding, to simplify compatibility-mode code. Additionally, | |
93 | * since 64-bit native code makes use of thread-local storage, the user-mode | |
94 | * GSbase must be restored. This sample includes two ways to do that-- (a) by | |
95 | * calling into a C implementation that associates the thread-local storage | |
96 | * pointer with a stack range (which will be unique for each thread.), and | |
97 | * (b) by storing the original GSbase in a block of memory installed into | |
98 | * GSbase before calling into compatibility-mode code. A special machdep | |
99 | * system call restores GSbase as needed. Note that the sample trampoline | |
100 | * does not save and restore %gs (or most other register state, so that is an | |
101 | * area that may be tailored to the application's requirements.) | |
102 | * (5) Once running in compatibility mode, should synchronous or asynchronous | |
103 | * exceptions occur, this sample shows how a mach exception handler (running | |
104 | * in a detached thread, handling exceptions for the entire task) can catch | |
105 | * such exceptions and manipulate thread state to perform recovery (or not.) | |
106 | * Other ways to handle exceptions include installing per-thread exception | |
107 | * servers. Alternatively, BSD signal handlers can be used. Note that once a | |
108 | * process installs a custom LDT, *ALL* future signal deliveries will include | |
109 | * ucontext pointers to mcontext structures that include enhanced thread | |
110 | * state embedded (e.g. the %ds, %es, %ss, and GSBase registers) [This assumes | |
111 | * that the SA_SIGINFO is passed to sigaction(2) when registering handlers]. | |
112 | * The mcontext size (part of the ucontext) can be used to differentiate between | |
113 | * different mcontext flavors (e.g. those with/without full thread state plus | |
114 | * x87 FP state, AVX state, or AVX2/3 state). | |
115 | */ | |
116 | ||
117 | /* | |
118 | * This test exercises the custom LDT functionality exposed via the i386_{get,set}_ldt | |
119 | * system calls. | |
120 | * | |
121 | * Tests include: | |
122 | * (1a) Exception handling (due to an exception or another thread sending a signal) while | |
123 | * running in compatibility mode; | |
124 | * (1b) Signal handling while running in compatibility mode; | |
125 | * (2) Thunking back to 64-bit mode and executing a framework function (e.g. printf) | |
126 | * (3) Ensuring that transitions to compatibility mode and back to 64-bit mode | |
127 | * do not negatively impact system calls and framework calls in 64-bit mode | |
128 | * (4) Use of thread_get_state / thread_set_state to configure a thread to | |
129 | * execute in compatibility mode with the proper LDT code segment (this is | |
130 | * effectively what the exception handler does when the passed-in new_state | |
131 | * is changed (or what the BSD signal handler return handling does when the | |
132 | * mcontext is modified).) | |
133 | * (5) Ensure that compatibility mode code cannot make system calls via sysenter or | |
134 | * old-style int {0x80..0x82}. | |
135 | * (6) Negative testing to ensure errors are returned if the consumer tries | |
136 | * to set a disallowed segment type / Long flag. [TBD] | |
137 | */ | |
138 | ||
139 | /* | |
140 | * Note that these addresses are not necessarily available due to ASLR, so | |
141 | * a robust implementation should determine the proper range to use via | |
142 | * another means. | |
143 | */ | |
0a7de745 A |
144 | #ifndef STANDALONE |
145 | /* libdarwintest needs LOTs of stack */ | |
146 | #endif | |
147 | #define FIXED_STACK_SIZE (PAGE_SIZE * 16) | |
0a7de745 A |
148 | #define FIXED_TRAMP_MAXLEN (PAGE_SIZE * 8) |
149 | ||
150 | #pragma pack(1) | |
151 | typedef struct { | |
152 | uint64_t off; | |
153 | uint16_t seg; | |
154 | } far_call_t; | |
155 | #pragma pack() | |
156 | ||
157 | typedef struct { | |
158 | uint64_t stack_base; | |
159 | uint64_t stack_limit; | |
160 | uint64_t GSbase; | |
161 | } stackaddr_to_gsbase_t; | |
162 | ||
163 | typedef struct thread_arg { | |
164 | pthread_mutex_t mutex; | |
165 | pthread_cond_t condvar; | |
166 | volatile boolean_t done; | |
167 | uint32_t compat_stackaddr; /* Compatibility mode stack address */ | |
168 | } thread_arg_t; | |
169 | ||
170 | typedef struct custom_tsd { | |
171 | struct custom_tsd * this_tsd_base; | |
172 | uint64_t orig_tsd_base; | |
173 | } custom_tsd_t; | |
174 | ||
175 | typedef uint64_t (*compat_tramp_t)(far_call_t *fcp, void *lowmemstk, uint64_t arg_for_32bit, | |
176 | uint64_t callback, uint64_t absolute_addr_of_thunk64); | |
177 | ||
178 | #define GS_RELATIVE volatile __attribute__((address_space(256))) | |
179 | static custom_tsd_t GS_RELATIVE *mytsd = (custom_tsd_t GS_RELATIVE *)0; | |
180 | ||
181 | static far_call_t input_desc = { .seg = COMPAT_MODE_CS_SELECTOR, .off = 0 }; | |
182 | static uint64_t stackAddr = 0; | |
183 | static compat_tramp_t thunkit = NULL; | |
184 | static uint64_t thunk64_addr; | |
cb323159 A |
185 | /* stack2gs[0] is initialized in map_lowmem_stack() */ |
186 | static stackaddr_to_gsbase_t stack2gs[] = { { 0 } }; | |
0a7de745 A |
187 | |
188 | extern int compat_mode_trampoline(far_call_t *, void *, uint64_t); | |
189 | extern void long_mode_trampoline(void); | |
190 | extern boolean_t mach_exc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); | |
191 | ||
192 | extern void code_32(void); | |
193 | ||
194 | kern_return_t catch_mach_exception_raise_state_identity(mach_port_t exception_port, | |
195 | mach_port_t thread, | |
196 | mach_port_t task, | |
197 | exception_type_t exception, | |
198 | mach_exception_data_t code, | |
199 | mach_msg_type_number_t code_count, | |
200 | int * flavor, | |
201 | thread_state_t old_state, | |
202 | mach_msg_type_number_t old_state_count, | |
203 | thread_state_t new_state, | |
204 | mach_msg_type_number_t * new_state_count); | |
205 | ||
206 | kern_return_t | |
207 | catch_mach_exception_raise_state(mach_port_t exception_port, | |
208 | exception_type_t exception, | |
209 | const mach_exception_data_t code, | |
210 | mach_msg_type_number_t codeCnt, | |
211 | int *flavor, | |
212 | const thread_state_t old_state, | |
213 | mach_msg_type_number_t old_stateCnt, | |
214 | thread_state_t new_state, | |
215 | mach_msg_type_number_t *new_stateCnt); | |
216 | ||
217 | kern_return_t | |
218 | catch_mach_exception_raise(mach_port_t exception_port, | |
219 | mach_port_t thread, | |
220 | mach_port_t task, | |
221 | exception_type_t exception, | |
222 | mach_exception_data_t code, | |
223 | mach_msg_type_number_t codeCnt, | |
224 | int *flavor, | |
225 | thread_state_t old_state, | |
226 | mach_msg_type_number_t old_stateCnt, | |
227 | thread_state_t new_state, | |
228 | mach_msg_type_number_t *new_stateCnt); | |
229 | ||
230 | extern void _thread_set_tsd_base(uint64_t); | |
231 | static uint64_t stack_range_to_GSbase(uint64_t stackptr, uint64_t GSbase); | |
232 | void restore_gsbase(uint64_t stackptr); | |
233 | ||
234 | static uint64_t | |
235 | get_gsbase(void) | |
236 | { | |
237 | struct thread_identifier_info tiinfo; | |
238 | unsigned int info_count = THREAD_IDENTIFIER_INFO_COUNT; | |
239 | kern_return_t kr; | |
240 | ||
241 | if ((kr = thread_info(mach_thread_self(), THREAD_IDENTIFIER_INFO, | |
242 | (thread_info_t) &tiinfo, &info_count)) != KERN_SUCCESS) { | |
243 | fprintf(stderr, "Could not get tsd base address. This will not end well.\n"); | |
244 | return 0; | |
245 | } | |
246 | ||
247 | return (uint64_t)tiinfo.thread_handle; | |
248 | } | |
249 | ||
250 | void | |
251 | restore_gsbase(uint64_t stackptr) | |
252 | { | |
253 | /* Restore GSbase so tsd is accessible in long mode */ | |
254 | uint64_t orig_GSbase = stack_range_to_GSbase(stackptr, 0); | |
255 | ||
256 | assert(orig_GSbase != 0); | |
257 | _thread_set_tsd_base(orig_GSbase); | |
258 | } | |
259 | ||
260 | /* | |
261 | * Though we've directed all exceptions through the catch_mach_exception_raise_state_identity | |
262 | * entry point, we still must provide these two other entry points, otherwise a linker error | |
263 | * will occur. | |
264 | */ | |
265 | kern_return_t | |
266 | catch_mach_exception_raise(mach_port_t exception_port, | |
267 | mach_port_t thread, | |
268 | mach_port_t task, | |
269 | exception_type_t exception, | |
270 | mach_exception_data_t code, | |
271 | mach_msg_type_number_t codeCnt, | |
272 | int *flavor, | |
273 | thread_state_t old_state, | |
274 | mach_msg_type_number_t old_stateCnt, | |
275 | thread_state_t new_state, | |
276 | mach_msg_type_number_t *new_stateCnt) | |
277 | { | |
278 | #pragma unused(exception_port, thread, task, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt) | |
279 | fprintf(stderr, "Unexpected exception handler called: %s\n", __func__); | |
280 | return KERN_FAILURE; | |
281 | } | |
282 | ||
283 | kern_return_t | |
284 | catch_mach_exception_raise_state(mach_port_t exception_port, | |
285 | exception_type_t exception, | |
286 | const mach_exception_data_t code, | |
287 | mach_msg_type_number_t codeCnt, | |
288 | int *flavor, | |
289 | const thread_state_t old_state, | |
290 | mach_msg_type_number_t old_stateCnt, | |
291 | thread_state_t new_state, | |
292 | mach_msg_type_number_t *new_stateCnt) | |
293 | { | |
294 | #pragma unused(exception_port, exception, code, codeCnt, flavor, old_state, old_stateCnt, new_state, new_stateCnt) | |
295 | fprintf(stderr, "Unexpected exception handler called: %s\n", __func__); | |
296 | return KERN_FAILURE; | |
297 | } | |
298 | ||
299 | static void | |
300 | handle_arithmetic_exception(_STRUCT_X86_THREAD_FULL_STATE64 *xtfs64, uint64_t *ip_skip_countp) | |
301 | { | |
302 | fprintf(stderr, "Caught divide-error exception\n"); | |
303 | fprintf(stderr, "cs=0x%x rip=0x%x gs=0x%x ss=0x%x rsp=0x%llx\n", | |
cb323159 A |
304 | (unsigned)xtfs64->__ss64.__cs, |
305 | (unsigned)xtfs64->__ss64.__rip, (unsigned)xtfs64->__ss64.__gs, | |
306 | (unsigned)xtfs64->__ss, xtfs64->__ss64.__rsp); | |
0a7de745 A |
307 | *ip_skip_countp = 2; |
308 | } | |
309 | ||
310 | static void | |
311 | handle_badinsn_exception(_STRUCT_X86_THREAD_FULL_STATE64 *xtfs64, uint64_t __unused *ip_skip_countp) | |
312 | { | |
313 | extern void first_invalid_opcode(void); | |
314 | extern void last_invalid_opcode(void); | |
315 | ||
316 | uint64_t start_addr = ((uintptr_t)first_invalid_opcode - (uintptr_t)code_32); | |
317 | uint64_t end_addr = ((uintptr_t)last_invalid_opcode - (uintptr_t)code_32); | |
318 | ||
319 | fprintf(stderr, "Caught invalid opcode exception\n"); | |
320 | fprintf(stderr, "cs=%x rip=%x gs=%x ss=0x%x rsp=0x%llx | handling between 0x%llx and 0x%llx\n", | |
cb323159 A |
321 | (unsigned)xtfs64->__ss64.__cs, |
322 | (unsigned)xtfs64->__ss64.__rip, (unsigned)xtfs64->__ss64.__gs, | |
323 | (unsigned)xtfs64->__ss, xtfs64->__ss64.__rsp, | |
0a7de745 A |
324 | start_addr, end_addr); |
325 | ||
326 | /* | |
327 | * We expect to handle 4 invalid opcode exceptions: | |
328 | * (1) sysenter | |
329 | * (2) int $0x80 | |
330 | * (3) int $0x81 | |
331 | * (4) int $0x82 | |
332 | * (Note that due to the way the invalid opcode indication was implemented, | |
333 | * %rip is already set to the next instruction.) | |
334 | */ | |
cb323159 | 335 | if (xtfs64->__ss64.__rip >= start_addr && xtfs64->__ss64.__rip <= end_addr) { |
0a7de745 A |
336 | /* |
337 | * On return from the failed sysenter, %cs is changed to the | |
338 | * sysenter code selector and %ss is set to 0x23, so switch them | |
339 | * back to sane values. | |
340 | */ | |
cb323159 A |
341 | if ((unsigned)xtfs64->__ss64.__cs == SYSENTER_SELECTOR) { |
342 | xtfs64->__ss64.__cs = COMPAT_MODE_CS_SELECTOR; | |
0a7de745 A |
343 | xtfs64->__ss = 0x23; /* XXX */ |
344 | } | |
345 | } | |
346 | } | |
347 | ||
348 | kern_return_t | |
349 | catch_mach_exception_raise_state_identity(mach_port_t exception_port, | |
350 | mach_port_t thread, | |
351 | mach_port_t task, | |
352 | exception_type_t exception, | |
353 | mach_exception_data_t code, | |
354 | mach_msg_type_number_t codeCnt, | |
355 | int * flavor, | |
356 | thread_state_t old_state, | |
357 | mach_msg_type_number_t old_stateCnt, | |
358 | thread_state_t new_state, | |
359 | mach_msg_type_number_t * new_stateCnt) | |
360 | { | |
361 | #pragma unused(exception_port, thread, task) | |
362 | ||
363 | _STRUCT_X86_THREAD_FULL_STATE64 *xtfs64 = (_STRUCT_X86_THREAD_FULL_STATE64 *)(void *)old_state; | |
364 | _STRUCT_X86_THREAD_FULL_STATE64 *new_xtfs64 = (_STRUCT_X86_THREAD_FULL_STATE64 *)(void *)new_state; | |
365 | uint64_t rip_skip_count = 0; | |
366 | ||
367 | /* | |
368 | * Check the exception code and thread state. | |
369 | * If we were executing 32-bit code (or 64-bit code on behalf of | |
370 | * 32-bit code), we could update the thread state to effectively longjmp | |
371 | * back to a safe location where the victim thread can recover. | |
372 | * Then again, we could return KERN_NOT_SUPPORTED and allow the process | |
373 | * to be nuked. | |
374 | */ | |
375 | ||
376 | switch (exception) { | |
377 | case EXC_ARITHMETIC: | |
378 | if (codeCnt >= 1 && code[0] == EXC_I386_DIV) { | |
379 | handle_arithmetic_exception(xtfs64, &rip_skip_count); | |
380 | } | |
381 | break; | |
382 | ||
383 | case EXC_BAD_INSTRUCTION: | |
384 | { | |
385 | if (codeCnt >= 1 && code[0] == EXC_I386_INVOP) { | |
386 | handle_badinsn_exception(xtfs64, &rip_skip_count); | |
387 | } | |
388 | break; | |
389 | } | |
390 | ||
391 | default: | |
392 | fprintf(stderr, "Unsupported catch_mach_exception_raise_state_identity: code 0x%llx sub 0x%llx\n", | |
393 | code[0], codeCnt > 1 ? code[1] : 0LL); | |
cb323159 A |
394 | fprintf(stderr, "flavor=%d %%cs=0x%x %%rip=0x%llx\n", *flavor, (unsigned)xtfs64->__ss64.__cs, |
395 | xtfs64->__ss64.__rip); | |
0a7de745 A |
396 | } |
397 | ||
398 | /* | |
399 | * If this exception happened in compatibility mode, | |
400 | * assume it was the intentional division-by-zero and set the | |
401 | * new state's cs register to just after the div instruction | |
402 | * to enable the thread to resume. | |
403 | */ | |
cb323159 | 404 | if ((unsigned)xtfs64->__ss64.__cs == COMPAT_MODE_CS_SELECTOR) { |
0a7de745 A |
405 | *new_stateCnt = old_stateCnt; |
406 | *new_xtfs64 = *xtfs64; | |
cb323159 A |
407 | new_xtfs64->__ss64.__rip += rip_skip_count; |
408 | fprintf(stderr, "new cs=0x%x rip=0x%llx\n", (unsigned)new_xtfs64->__ss64.__cs, | |
409 | new_xtfs64->__ss64.__rip); | |
0a7de745 A |
410 | return KERN_SUCCESS; |
411 | } else { | |
412 | return KERN_NOT_SUPPORTED; | |
413 | } | |
414 | } | |
415 | ||
416 | static void * | |
417 | handle_exceptions(void *arg) | |
418 | { | |
419 | mach_port_t ePort = (mach_port_t)arg; | |
420 | kern_return_t kret; | |
421 | ||
422 | kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, ePort, 0); | |
423 | if (kret != KERN_SUCCESS) { | |
424 | fprintf(stderr, "mach_msg_server: %s (%d)", mach_error_string(kret), kret); | |
425 | } | |
426 | ||
427 | return NULL; | |
428 | } | |
429 | ||
430 | static void | |
431 | init_task_exception_server(void) | |
432 | { | |
433 | kern_return_t kr; | |
434 | task_t me = mach_task_self(); | |
435 | pthread_t handler_thread; | |
436 | pthread_attr_t attr; | |
437 | mach_port_t ePort; | |
438 | ||
439 | kr = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &ePort); | |
440 | if (kr != KERN_SUCCESS) { | |
441 | fprintf(stderr, "allocate receive right: %d\n", kr); | |
442 | return; | |
443 | } | |
444 | ||
445 | kr = mach_port_insert_right(me, ePort, ePort, MACH_MSG_TYPE_MAKE_SEND); | |
446 | if (kr != KERN_SUCCESS) { | |
447 | fprintf(stderr, "insert right into port=[%d]: %d\n", ePort, kr); | |
448 | return; | |
449 | } | |
450 | ||
451 | kr = task_set_exception_ports(me, EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC, ePort, | |
452 | (exception_behavior_t)(EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES), x86_THREAD_FULL_STATE64); | |
453 | if (kr != KERN_SUCCESS) { | |
454 | fprintf(stderr, "abort: error setting task exception ports on task=[%d], handler=[%d]: %d\n", me, ePort, kr); | |
455 | exit(1); | |
456 | } | |
457 | ||
458 | pthread_attr_init(&attr); | |
459 | pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
460 | ||
461 | if (pthread_create(&handler_thread, &attr, handle_exceptions, (void *)(uintptr_t)ePort) != 0) { | |
462 | perror("pthread create error"); | |
463 | return; | |
464 | } | |
465 | ||
466 | pthread_attr_destroy(&attr); | |
467 | } | |
468 | ||
469 | static union ldt_entry *descs = 0; | |
470 | static uint64_t idx; | |
471 | static int saw_ud2 = 0; | |
472 | static boolean_t ENV_set_ldt_in_sighandler = FALSE; | |
473 | ||
474 | static void | |
475 | signal_handler(int signo, siginfo_t *sinfop, void *ucontext) | |
476 | { | |
477 | uint64_t rip_skip_count = 0; | |
478 | ucontext_t *uctxp = (ucontext_t *)ucontext; | |
479 | union { | |
480 | _STRUCT_MCONTEXT_AVX512_64 *avx512_basep; | |
481 | _STRUCT_MCONTEXT_AVX512_64_FULL *avx512_fullp; | |
482 | _STRUCT_MCONTEXT_AVX64 *avx64_basep; | |
483 | _STRUCT_MCONTEXT_AVX64_FULL *avx64_fullp; | |
484 | _STRUCT_MCONTEXT64 *fp_basep; | |
485 | _STRUCT_MCONTEXT64_FULL *fp_fullp; | |
486 | } mctx; | |
487 | ||
488 | mctx.fp_fullp = (_STRUCT_MCONTEXT64_FULL *)uctxp->uc_mcontext; | |
489 | ||
490 | /* | |
491 | * Note that GSbase must be restored before calling into any frameworks | |
492 | * that might access anything %gs-relative (e.g. TSD) if the signal | |
493 | * handler was triggered while the thread was running with a non-default | |
494 | * (system-established) GSbase. | |
495 | */ | |
496 | ||
497 | if ((signo != SIGFPE && signo != SIGILL) || sinfop->si_signo != signo) { | |
498 | #ifndef STANDALONE | |
499 | T_ASSERT_FAIL("Unexpected signal %d\n", signo); | |
500 | #else | |
cb323159 | 501 | restore_gsbase(mctx.fp_fullp->__ss.__ss64.__rsp); |
0a7de745 A |
502 | fprintf(stderr, "Not handling signal %d\n", signo); |
503 | abort(); | |
504 | #endif | |
505 | } | |
506 | ||
507 | if (uctxp->uc_mcsize == sizeof(_STRUCT_MCONTEXT_AVX512_64) || | |
508 | uctxp->uc_mcsize == sizeof(_STRUCT_MCONTEXT_AVX64) || | |
509 | uctxp->uc_mcsize == sizeof(_STRUCT_MCONTEXT64)) { | |
510 | _STRUCT_X86_THREAD_STATE64 *ss64 = &mctx.fp_basep->__ss; | |
511 | ||
512 | /* | |
513 | * The following block is an illustration of what NOT to do. | |
514 | * Configuring an LDT for the first time in a signal handler | |
515 | * will likely cause the process to crash. | |
516 | */ | |
517 | if (ENV_set_ldt_in_sighandler == TRUE && !saw_ud2) { | |
518 | /* Set the LDT: */ | |
519 | int cnt = i386_set_ldt((int)idx, &descs[idx], 1); | |
520 | if (cnt != (int)idx) { | |
521 | #ifdef DEBUG | |
cb323159 | 522 | fprintf(stderr, "i386_set_ldt unexpectedly returned %d (errno = %s)\n", cnt, strerror(errno)); |
0a7de745 A |
523 | #endif |
524 | #ifndef STANDALONE | |
cb323159 | 525 | T_LOG("i386_set_ldt unexpectedly returned %d (errno: %s)\n", cnt, strerror(errno)); |
0a7de745 A |
526 | T_ASSERT_FAIL("i386_set_ldt failure"); |
527 | #else | |
528 | exit(1); | |
529 | #endif | |
530 | } | |
531 | #ifdef DEBUG | |
532 | printf("i386_set_ldt returned %d\n", cnt); | |
533 | #endif | |
534 | ss64->__rip += 2; /* ud2 is 2 bytes */ | |
535 | ||
536 | saw_ud2 = 1; | |
537 | ||
538 | /* | |
539 | * When we return here, the sigreturn processing code will try to copy a FULL | |
540 | * thread context from the signal stack, which will likely cause the resumed | |
541 | * thread to fault and be terminated. | |
542 | */ | |
543 | return; | |
544 | } | |
545 | ||
546 | restore_gsbase(ss64->__rsp); | |
547 | ||
548 | /* | |
549 | * If we're in this block, either we are dispatching a signal received | |
550 | * before we installed a custom LDT or we are on a kernel without | |
551 | * BSD-signalling-sending-full-thread-state support. It's likely the latter case. | |
552 | */ | |
553 | #ifndef STANDALONE | |
554 | T_ASSERT_FAIL("This system doesn't support BSD signals with full thread state."); | |
555 | #else | |
556 | fprintf(stderr, "This system doesn't support BSD signals with full thread state. Aborting.\n"); | |
557 | abort(); | |
558 | #endif | |
559 | } else if (uctxp->uc_mcsize == sizeof(_STRUCT_MCONTEXT_AVX512_64_FULL) || | |
560 | uctxp->uc_mcsize == sizeof(_STRUCT_MCONTEXT_AVX64_FULL) || | |
561 | uctxp->uc_mcsize == sizeof(_STRUCT_MCONTEXT64_FULL)) { | |
562 | _STRUCT_X86_THREAD_FULL_STATE64 *ss64 = &mctx.fp_fullp->__ss; | |
563 | ||
564 | /* | |
565 | * Since we're handing this signal on the same thread, we may need to | |
566 | * restore GSbase. | |
567 | */ | |
cb323159 | 568 | uint64_t orig_gsbase = stack_range_to_GSbase(ss64->__ss64.__rsp, 0); |
0a7de745 | 569 | if (orig_gsbase != 0 && orig_gsbase != ss64->__gsbase) { |
cb323159 | 570 | restore_gsbase(ss64->__ss64.__rsp); |
0a7de745 A |
571 | } |
572 | ||
573 | if (signo == SIGFPE) { | |
574 | handle_arithmetic_exception(ss64, &rip_skip_count); | |
575 | } else if (signo == SIGILL) { | |
576 | handle_badinsn_exception(ss64, &rip_skip_count); | |
577 | } | |
578 | ||
579 | /* | |
580 | * If this exception happened in compatibility mode, | |
581 | * assume it was the intentional division-by-zero and set the | |
582 | * new state's cs register to just after the div instruction | |
583 | * to enable the thread to resume. | |
584 | */ | |
cb323159 A |
585 | if ((unsigned)ss64->__ss64.__cs == COMPAT_MODE_CS_SELECTOR) { |
586 | ss64->__ss64.__rip += rip_skip_count; | |
587 | fprintf(stderr, "new cs=0x%x rip=0x%llx\n", (unsigned)ss64->__ss64.__cs, | |
588 | ss64->__ss64.__rip); | |
0a7de745 A |
589 | } |
590 | } else { | |
591 | _STRUCT_X86_THREAD_STATE64 *ss64 = &mctx.fp_basep->__ss; | |
592 | ||
593 | restore_gsbase(ss64->__rsp); | |
594 | #ifndef STANDALONE | |
595 | T_ASSERT_FAIL("Unknown mcontext size %lu: Aborting.", uctxp->uc_mcsize); | |
596 | #else | |
597 | fprintf(stderr, "Unknown mcontext size %lu: Aborting.\n", uctxp->uc_mcsize); | |
598 | abort(); | |
599 | #endif | |
600 | } | |
601 | } | |
602 | ||
603 | static void | |
604 | setup_signal_handling(void) | |
605 | { | |
606 | int rv; | |
607 | ||
608 | struct sigaction sa = { | |
609 | .__sigaction_u = { .__sa_sigaction = signal_handler }, | |
610 | .sa_flags = SA_SIGINFO | |
611 | }; | |
612 | ||
613 | sigfillset(&sa.sa_mask); | |
614 | ||
615 | rv = sigaction(SIGFPE, &sa, NULL); | |
616 | if (rv != 0) { | |
617 | #ifndef STANDALONE | |
618 | T_ASSERT_FAIL("Failed to configure SIGFPE signal handler\n"); | |
619 | #else | |
620 | fprintf(stderr, "Failed to configure SIGFPE signal handler\n"); | |
621 | abort(); | |
622 | #endif | |
623 | } | |
624 | ||
625 | rv = sigaction(SIGILL, &sa, NULL); | |
626 | if (rv != 0) { | |
627 | #ifndef STANDALONE | |
628 | T_ASSERT_FAIL("Failed to configure SIGILL signal handler\n"); | |
629 | #else | |
630 | fprintf(stderr, "Failed to configure SIGILL signal handler\n"); | |
631 | abort(); | |
632 | #endif | |
633 | } | |
634 | } | |
635 | ||
636 | static void | |
637 | teardown_signal_handling(void) | |
638 | { | |
639 | if (signal(SIGFPE, SIG_DFL) == SIG_ERR) { | |
640 | #ifndef STANDALONE | |
641 | T_ASSERT_FAIL("Error resetting SIGFPE signal disposition\n"); | |
642 | #else | |
643 | fprintf(stderr, "Error resetting SIGFPE signal disposition\n"); | |
644 | abort(); | |
645 | #endif | |
646 | } | |
647 | ||
648 | if (signal(SIGILL, SIG_DFL) == SIG_ERR) { | |
649 | #ifndef STANDALONE | |
650 | T_ASSERT_FAIL("Error resetting SIGILL signal disposition\n"); | |
651 | #else | |
652 | fprintf(stderr, "Error resetting SIGILL signal disposition\n"); | |
653 | abort(); | |
654 | #endif | |
655 | } | |
656 | } | |
657 | ||
658 | #ifdef DEBUG | |
659 | static void | |
660 | dump_desc(union ldt_entry *entp) | |
661 | { | |
662 | printf("base %p lim %p type 0x%x dpl %x present %x opsz %x granular %x\n", | |
663 | (void *)(uintptr_t)(entp->code.base00 + (entp->code.base16 << 16) + (entp->code.base24 << 24)), | |
664 | (void *)(uintptr_t)(entp->code.limit00 + (entp->code.limit16 << 16)), | |
665 | entp->code.type, | |
666 | entp->code.dpl, | |
667 | entp->code.present, | |
668 | entp->code.opsz, | |
669 | entp->code.granular); | |
670 | } | |
671 | #endif | |
672 | ||
673 | static int | |
674 | map_lowmem_stack(void **lowmemstk) | |
675 | { | |
cb323159 A |
676 | void *addr; |
677 | int err; | |
0a7de745 | 678 | |
cb323159 A |
679 | if ((addr = mmap(0, FIXED_STACK_SIZE + PAGE_SIZE, PROT_READ | PROT_WRITE, |
680 | MAP_32BIT | MAP_PRIVATE | MAP_ANON, -1, 0)) == MAP_FAILED) { | |
0a7de745 A |
681 | return errno; |
682 | } | |
683 | ||
cb323159 A |
684 | if ((uintptr_t)addr > 0xFFFFF000ULL) { |
685 | /* Error: This kernel does not support MAP_32BIT or there's a bug. */ | |
686 | #ifndef STANDALONE | |
687 | T_ASSERT_FAIL("%s: failed to map a 32-bit-accessible stack", __func__); | |
688 | #else | |
689 | fprintf(stderr, "This kernel returned a virtual address > 4G (%p) despite MAP_32BIT. Aborting.\n", addr); | |
690 | exit(1); | |
691 | #endif | |
692 | } | |
693 | ||
694 | /* Enforce one page of redzone at the bottom of the stack */ | |
695 | if (mprotect(addr, PAGE_SIZE, PROT_NONE) < 0) { | |
696 | err = errno; | |
697 | (void) munmap(addr, FIXED_STACK_SIZE + PAGE_SIZE); | |
698 | return err; | |
0a7de745 A |
699 | } |
700 | ||
701 | if (lowmemstk) { | |
cb323159 A |
702 | stack2gs[0].stack_base = (uintptr_t)addr + PAGE_SIZE; |
703 | stack2gs[0].stack_limit = stack2gs[0].stack_base + FIXED_STACK_SIZE; | |
704 | *lowmemstk = (void *)((uintptr_t)addr + PAGE_SIZE); | |
0a7de745 A |
705 | } |
706 | ||
707 | return 0; | |
708 | } | |
709 | ||
710 | static int | |
cb323159 | 711 | map_32bit_code_impl(uint8_t *code_src, size_t code_len, void **codeptr, |
0a7de745 A |
712 | size_t szlimit) |
713 | { | |
714 | void *addr; | |
715 | size_t sz = (size_t)P2ROUNDUP(code_len, (unsigned)PAGE_SIZE); | |
716 | ||
717 | if (code_len > szlimit) { | |
718 | return E2BIG; | |
719 | } | |
720 | ||
721 | #ifdef DEBUG | |
cb323159 | 722 | printf("size = %lu, szlimit = %u\n", sz, (unsigned)szlimit); |
0a7de745 A |
723 | #endif |
724 | ||
cb323159 A |
725 | if ((addr = mmap(0, sz, PROT_READ | PROT_WRITE | PROT_EXEC, |
726 | MAP_32BIT | MAP_PRIVATE | MAP_ANON, -1, 0)) == MAP_FAILED) { | |
0a7de745 A |
727 | return errno; |
728 | } | |
729 | ||
cb323159 A |
730 | if ((uintptr_t)addr > 0xFFFFF000ULL) { |
731 | /* Error: This kernel does not support MAP_32BIT or there's a bug. */ | |
732 | #ifndef STANDALONE | |
733 | T_ASSERT_FAIL("%s: failed to map a 32-bit-accessible trampoline", __func__); | |
734 | #else | |
735 | fprintf(stderr, "This kernel returned a virtual address > 4G (%p) despite MAP_32BIT. Aborting.\n", addr); | |
736 | exit(1); | |
737 | #endif | |
738 | } | |
739 | ||
0a7de745 A |
740 | #ifdef DEBUG |
741 | printf("Mapping code @%p..%p => %p..%p\n", (void *)code_src, | |
742 | (void *)((uintptr_t)code_src + (unsigned)code_len), | |
743 | addr, (void *)((uintptr_t)addr + (unsigned)code_len)); | |
744 | #endif | |
745 | ||
746 | bcopy(code_src, addr, code_len); | |
747 | ||
748 | /* Fill the rest of the page with NOPs */ | |
cb323159 A |
749 | if ((sz - code_len) > 0) { |
750 | memset((void *)((uintptr_t)addr + code_len), 0x90, sz - code_len); | |
751 | } | |
0a7de745 A |
752 | |
753 | if (codeptr) { | |
754 | *codeptr = addr; | |
755 | } | |
756 | ||
757 | return 0; | |
758 | } | |
759 | ||
760 | static int | |
761 | map_32bit_trampoline(compat_tramp_t *lowmemtrampp) | |
762 | { | |
763 | extern int compat_mode_trampoline_len; | |
764 | ||
765 | return map_32bit_code_impl((uint8_t *)&compat_mode_trampoline, | |
766 | (size_t)compat_mode_trampoline_len, (void **)lowmemtrampp, | |
cb323159 | 767 | FIXED_TRAMP_MAXLEN); |
0a7de745 A |
768 | } |
769 | ||
770 | static uint64_t | |
771 | stack_range_to_GSbase(uint64_t stackptr, uint64_t GSbase) | |
772 | { | |
773 | unsigned long i; | |
774 | ||
775 | for (i = 0; i < sizeof(stack2gs) / sizeof(stack2gs[0]); i++) { | |
776 | if (stackptr >= stack2gs[i].stack_base && | |
777 | stackptr < stack2gs[i].stack_limit) { | |
778 | if (GSbase != 0) { | |
779 | #ifdef DEBUG | |
780 | fprintf(stderr, "Updated gsbase for stack at 0x%llx..0x%llx to 0x%llx\n", | |
781 | stack2gs[i].stack_base, stack2gs[i].stack_limit, GSbase); | |
782 | #endif | |
783 | stack2gs[i].GSbase = GSbase; | |
784 | } | |
785 | return stack2gs[i].GSbase; | |
786 | } | |
787 | } | |
788 | return 0; | |
789 | } | |
790 | ||
791 | static uint64_t | |
792 | call_compatmode(uint32_t stackaddr, uint64_t compat_arg, uint64_t callback) | |
793 | { | |
794 | uint64_t rv; | |
795 | ||
796 | /* | |
797 | * Depending on how this is used, this allocation may need to be | |
798 | * made with an allocator that returns virtual addresses below 4G. | |
799 | */ | |
800 | custom_tsd_t *new_GSbase = malloc(PAGE_SIZE); | |
801 | ||
802 | /* | |
803 | * Change the GSbase (so things like printf will fail unless GSbase is | |
804 | * restored) | |
805 | */ | |
806 | if (new_GSbase != NULL) { | |
807 | #ifdef DEBUG | |
808 | fprintf(stderr, "Setting new GS base: %p\n", (void *)new_GSbase); | |
809 | #endif | |
810 | new_GSbase->this_tsd_base = new_GSbase; | |
811 | new_GSbase->orig_tsd_base = get_gsbase(); | |
812 | _thread_set_tsd_base((uintptr_t)new_GSbase); | |
813 | } else { | |
814 | #ifndef STANDALONE | |
815 | T_ASSERT_FAIL("Failed to allocate a page for new GSbase"); | |
816 | #else | |
817 | fprintf(stderr, "Failed to allocate a page for new GSbase"); | |
818 | abort(); | |
819 | #endif | |
820 | } | |
821 | ||
822 | rv = thunkit(&input_desc, (void *)(uintptr_t)stackaddr, compat_arg, | |
823 | callback, thunk64_addr); | |
824 | ||
825 | restore_gsbase(stackaddr); | |
826 | ||
827 | free(new_GSbase); | |
828 | ||
829 | return rv; | |
830 | } | |
831 | ||
832 | static uint64_t | |
833 | get_cursp(void) | |
834 | { | |
835 | uint64_t curstk; | |
836 | __asm__ __volatile__ ("movq %%rsp, %0" : "=r" (curstk) :: "memory"); | |
837 | return curstk; | |
838 | } | |
839 | ||
840 | static void | |
841 | hello_from_32bit(void) | |
842 | { | |
843 | uint64_t cur_tsd_base = (uint64_t)(uintptr_t)mytsd->this_tsd_base; | |
844 | restore_gsbase(get_cursp()); | |
845 | ||
846 | printf("Hello on behalf of 32-bit compatibility mode!\n"); | |
847 | ||
848 | _thread_set_tsd_base(cur_tsd_base); | |
849 | } | |
850 | ||
851 | /* | |
852 | * Thread for executing 32-bit code | |
853 | */ | |
854 | static void * | |
855 | thread_32bit(void *arg) | |
856 | { | |
857 | thread_arg_t *targp = (thread_arg_t *)arg; | |
858 | uint64_t cthread_self = 0; | |
859 | ||
860 | /* Save the GSbase for context switch back to 64-bit mode */ | |
861 | cthread_self = get_gsbase(); | |
862 | ||
863 | /* | |
864 | * Associate GSbase with the compat-mode stack (which will be used for long mode | |
865 | * thunk calls as well.) | |
866 | */ | |
867 | (void)stack_range_to_GSbase(targp->compat_stackaddr, cthread_self); | |
868 | ||
869 | #ifdef DEBUG | |
870 | printf("[thread %p] tsd base => %p\n", (void *)pthread_self(), (void *)cthread_self); | |
871 | #endif | |
872 | ||
873 | pthread_mutex_lock(&targp->mutex); | |
874 | ||
875 | do { | |
876 | if (targp->done == FALSE) { | |
877 | pthread_cond_wait(&targp->condvar, &targp->mutex); | |
878 | } | |
879 | ||
880 | /* Finally, execute the test */ | |
881 | if (call_compatmode(targp->compat_stackaddr, 0, | |
882 | (uint64_t)&hello_from_32bit) == 1) { | |
883 | printf("32-bit code test passed\n"); | |
884 | } else { | |
885 | printf("32-bit code test failed\n"); | |
886 | } | |
887 | } while (targp->done == FALSE); | |
888 | ||
889 | pthread_mutex_unlock(&targp->mutex); | |
890 | ||
891 | return 0; | |
892 | } | |
893 | ||
894 | static void | |
895 | join_32bit_thread(pthread_t *thridp, thread_arg_t *cmargp) | |
896 | { | |
897 | (void)pthread_mutex_lock(&cmargp->mutex); | |
898 | cmargp->done = TRUE; | |
899 | (void)pthread_cond_signal(&cmargp->condvar); | |
900 | (void)pthread_mutex_unlock(&cmargp->mutex); | |
901 | (void)pthread_join(*thridp, NULL); | |
902 | *thridp = 0; | |
903 | } | |
904 | ||
905 | static int | |
906 | create_worker_thread(thread_arg_t *cmargp, uint32_t stackaddr, pthread_t *cmthreadp) | |
907 | { | |
908 | *cmargp = (thread_arg_t) { .mutex = PTHREAD_MUTEX_INITIALIZER, | |
909 | .condvar = PTHREAD_COND_INITIALIZER, | |
910 | .done = FALSE, | |
911 | .compat_stackaddr = stackaddr }; | |
912 | ||
913 | return pthread_create(cmthreadp, NULL, thread_32bit, cmargp); | |
914 | } | |
915 | ||
916 | static void | |
917 | ldt64_test_setup(pthread_t *cmthreadp, thread_arg_t *cmargp, boolean_t setldt_in_sighandler) | |
918 | { | |
919 | extern void thunk64(void); | |
920 | extern void thunk64_movabs(void); | |
921 | int cnt = 0, err; | |
922 | void *addr; | |
923 | uintptr_t code_addr; | |
924 | uintptr_t thunk64_movabs_addr; | |
0a7de745 A |
925 | |
926 | descs = malloc(sizeof(union ldt_entry) * 256); | |
927 | if (descs == 0) { | |
928 | #ifndef STANDALONE | |
929 | T_ASSERT_FAIL("Could not allocate descriptor storage"); | |
930 | #else | |
931 | fprintf(stderr, "Could not allocate descriptor storage\n"); | |
932 | abort(); | |
933 | #endif | |
934 | } | |
935 | ||
0a7de745 A |
936 | #ifdef DEBUG |
937 | printf("32-bit code is at %p\n", (void *)&code_32); | |
938 | #endif | |
939 | ||
940 | if ((err = map_lowmem_stack(&addr)) != 0) { | |
0a7de745 | 941 | #ifndef STANDALONE |
cb323159 | 942 | T_ASSERT_FAIL("failed to mmap lowmem stack: %s", strerror(err)); |
0a7de745 | 943 | #else |
cb323159 | 944 | fprintf(stderr, "Failed to mmap lowmem stack: %s\n", strerror(err)); |
0a7de745 A |
945 | exit(1); |
946 | #endif | |
947 | } | |
948 | ||
949 | stackAddr = (uintptr_t)addr + FIXED_STACK_SIZE - 16; | |
950 | #ifdef DEBUG | |
951 | printf("lowstack addr = %p\n", (void *)stackAddr); | |
952 | #endif | |
953 | ||
0a7de745 | 954 | if ((err = map_32bit_trampoline(&thunkit)) != 0) { |
0a7de745 A |
955 | #ifndef STANDALONE |
956 | T_LOG("Failed to map trampoline into lowmem: %s\n", strerror(err)); | |
957 | T_ASSERT_FAIL("Failed to map trampoline into lowmem"); | |
958 | #else | |
cb323159 | 959 | fprintf(stderr, "Failed to map trampoline into lowmem: %s\n", strerror(err)); |
0a7de745 A |
960 | exit(1); |
961 | #endif | |
962 | } | |
963 | ||
964 | /* | |
965 | * Store long_mode_trampoline's address into the constant part of the movabs | |
966 | * instruction in thunk64 | |
967 | */ | |
968 | thunk64_movabs_addr = (uintptr_t)thunkit + ((uintptr_t)thunk64_movabs - (uintptr_t)compat_mode_trampoline); | |
969 | *((uint64_t *)(thunk64_movabs_addr + 2)) = (uint64_t)&long_mode_trampoline; | |
970 | ||
971 | bzero(descs, sizeof(union ldt_entry) * 256); | |
972 | ||
973 | if ((cnt = i386_get_ldt(0, descs, 1)) <= 0) { | |
0a7de745 | 974 | #ifndef STANDALONE |
cb323159 | 975 | T_LOG("i386_get_ldt unexpectedly returned %d (errno: %s)\n", cnt, strerror(errno)); |
0a7de745 A |
976 | T_ASSERT_FAIL("i386_get_ldt failure"); |
977 | #else | |
cb323159 | 978 | fprintf(stderr, "i386_get_ldt unexpectedly returned %d (errno: %s)\n", cnt, strerror(errno)); |
0a7de745 A |
979 | exit(1); |
980 | #endif | |
981 | } | |
982 | ||
983 | #ifdef DEBUG | |
984 | printf("i386_get_ldt returned %d\n", cnt); | |
985 | #endif | |
986 | ||
987 | idx = (unsigned)cnt; /* Put the desired descriptor in the first available slot */ | |
988 | ||
989 | /* | |
990 | * code_32's address for the purposes of this descriptor is the base mapped address of | |
991 | * the thunkit function + the offset of code_32 from compat_mode_trampoline. | |
992 | */ | |
993 | code_addr = (uintptr_t)thunkit + ((uintptr_t)code_32 - (uintptr_t)compat_mode_trampoline); | |
994 | thunk64_addr = (uintptr_t)thunkit + ((uintptr_t)thunk64 - (uintptr_t)compat_mode_trampoline); | |
995 | ||
996 | /* Initialize desired descriptor */ | |
997 | descs[idx].code.limit00 = (unsigned short)(((code_addr >> 12) + 1) & 0xFFFF); | |
998 | descs[idx].code.limit16 = (unsigned char)((((code_addr >> 12) + 1) >> 16) & 0xF); | |
999 | descs[idx].code.base00 = (unsigned short)((code_addr) & 0xFFFF); | |
1000 | descs[idx].code.base16 = (unsigned char)((code_addr >> 16) & 0xFF); | |
1001 | descs[idx].code.base24 = (unsigned char)((code_addr >> 24) & 0xFF); | |
1002 | descs[idx].code.type = DESC_CODE_READ; | |
1003 | descs[idx].code.opsz = DESC_CODE_32B; | |
1004 | descs[idx].code.granular = DESC_GRAN_PAGE; | |
1005 | descs[idx].code.dpl = 3; | |
1006 | descs[idx].code.present = 1; | |
1007 | ||
1008 | if (setldt_in_sighandler == FALSE) { | |
1009 | /* Set the LDT: */ | |
1010 | cnt = i386_set_ldt((int)idx, &descs[idx], 1); | |
1011 | if (cnt != (int)idx) { | |
0a7de745 | 1012 | #ifndef STANDALONE |
cb323159 | 1013 | T_LOG("i386_set_ldt unexpectedly returned %d (errno: %s)\n", cnt, strerror(errno)); |
0a7de745 A |
1014 | T_ASSERT_FAIL("i386_set_ldt failure"); |
1015 | #else | |
cb323159 | 1016 | fprintf(stderr, "i386_set_ldt unexpectedly returned %d (errno: %s)\n", cnt, strerror(errno)); |
0a7de745 A |
1017 | exit(1); |
1018 | #endif | |
1019 | } | |
1020 | #ifdef DEBUG | |
1021 | printf("i386_set_ldt returned %d\n", cnt); | |
1022 | #endif | |
1023 | } else { | |
1024 | __asm__ __volatile__ ("ud2" ::: "memory"); | |
1025 | } | |
1026 | ||
1027 | ||
1028 | /* Read back the LDT to ensure it was set properly */ | |
1029 | if ((cnt = i386_get_ldt(0, descs, (int)idx)) > 0) { | |
1030 | #ifdef DEBUG | |
1031 | for (int i = 0; i < cnt; i++) { | |
1032 | dump_desc(&descs[i]); | |
1033 | } | |
1034 | #endif | |
1035 | } else { | |
0a7de745 | 1036 | #ifndef STANDALONE |
cb323159 | 1037 | T_LOG("i386_get_ldt unexpectedly returned %d (errno: %s)\n", cnt, strerror(errno)); |
0a7de745 A |
1038 | T_ASSERT_FAIL("i386_get_ldt failure"); |
1039 | #else | |
cb323159 | 1040 | fprintf(stderr, "i386_get_ldt unexpectedly returned %d (errno: %s)\n", cnt, strerror(errno)); |
0a7de745 A |
1041 | exit(1); |
1042 | #endif | |
1043 | } | |
1044 | ||
1045 | free(descs); | |
cb323159 A |
1046 | |
1047 | if ((err = create_worker_thread(cmargp, (uint32_t)stackAddr, cmthreadp)) != 0) { | |
1048 | #ifdef DEBUG | |
1049 | fprintf(stderr, "Fatal: Could not create thread: %s\n", strerror(err)); | |
1050 | #endif | |
1051 | #ifndef STANDALONE | |
1052 | T_LOG("Fatal: Could not create thread: %s\n", strerror(err)); | |
1053 | T_ASSERT_FAIL("Thread creation failure"); | |
1054 | #else | |
1055 | exit(1); | |
1056 | #endif | |
1057 | } | |
0a7de745 A |
1058 | } |
1059 | ||
1060 | #ifdef STANDALONE | |
1061 | static void | |
1062 | test_ldt64_with_bsdsig(void) | |
1063 | #else | |
1064 | /* | |
1065 | * Main test declarations | |
1066 | */ | |
1067 | T_DECL(ldt64_with_bsd_sighandling, | |
1068 | "Ensures that a 64-bit process can create LDT entries and can execute code in " | |
1069 | "compatibility mode with BSD signal handling", | |
1070 | T_META_TIMEOUT(NORMAL_RUN_TIME + TIMEOUT_OVERHEAD)) | |
1071 | #endif | |
1072 | { | |
1073 | pthread_t cmthread; | |
1074 | thread_arg_t cmarg; | |
1075 | ||
f427ee49 A |
1076 | int translated = 0; |
1077 | size_t translated_size = sizeof(int); | |
1078 | ||
1079 | sysctlbyname("sysctl.proc_translated", &translated, &translated_size, NULL, 0); | |
1080 | ||
1081 | if (translated) { | |
1082 | T_SKIP("Skipping this test because it is translated"); | |
1083 | } | |
1084 | ||
0a7de745 A |
1085 | setup_signal_handling(); |
1086 | ||
1087 | #ifndef STANDALONE | |
1088 | T_SETUPBEGIN; | |
1089 | #endif | |
1090 | ENV_set_ldt_in_sighandler = (getenv("LDT_SET_IN_SIGHANDLER") != NULL) ? TRUE : FALSE; | |
1091 | ldt64_test_setup(&cmthread, &cmarg, ENV_set_ldt_in_sighandler); | |
1092 | #ifndef STANDALONE | |
1093 | T_SETUPEND; | |
1094 | #endif | |
1095 | ||
1096 | join_32bit_thread(&cmthread, &cmarg); | |
1097 | ||
1098 | teardown_signal_handling(); | |
1099 | ||
1100 | #ifndef STANDALONE | |
1101 | T_PASS("Successfully completed ldt64 test with BSD signal handling"); | |
1102 | #else | |
1103 | fprintf(stderr, "PASSED: ldt64_with_bsd_signal_handling\n"); | |
1104 | #endif | |
1105 | } | |
1106 | ||
1107 | #ifdef STANDALONE | |
1108 | static void | |
1109 | test_ldt64_with_machexc(void) | |
1110 | #else | |
1111 | T_DECL(ldt64_with_mach_exception_handling, | |
1112 | "Ensures that a 64-bit process can create LDT entries and can execute code in " | |
1113 | "compatibility mode with Mach exception handling", | |
1114 | T_META_TIMEOUT(NORMAL_RUN_TIME + TIMEOUT_OVERHEAD)) | |
1115 | #endif | |
1116 | { | |
1117 | pthread_t cmthread; | |
1118 | thread_arg_t cmarg; | |
1119 | ||
f427ee49 A |
1120 | int translated = 0; |
1121 | size_t translated_size = sizeof(int); | |
1122 | ||
1123 | sysctlbyname("sysctl.proc_translated", &translated, &translated_size, NULL, 0); | |
1124 | ||
1125 | if (translated) { | |
1126 | T_SKIP("Skipping this test because it is translated"); | |
1127 | } | |
1128 | ||
0a7de745 A |
1129 | #ifndef STANDALONE |
1130 | T_SETUPBEGIN; | |
1131 | #endif | |
1132 | ldt64_test_setup(&cmthread, &cmarg, FALSE); | |
1133 | #ifndef STANDALONE | |
1134 | T_SETUPEND; | |
1135 | #endif | |
1136 | ||
1137 | /* Now repeat with Mach exception handling */ | |
1138 | init_task_exception_server(); | |
1139 | ||
1140 | join_32bit_thread(&cmthread, &cmarg); | |
1141 | ||
1142 | #ifndef STANDALONE | |
1143 | T_PASS("Successfully completed ldt64 test with mach exception handling"); | |
1144 | #else | |
1145 | fprintf(stderr, "PASSED: ldt64_with_mach_exception_handling\n"); | |
1146 | #endif | |
1147 | } | |
1148 | ||
1149 | #ifdef STANDALONE | |
1150 | int | |
1151 | main(int __unused argc, char ** __unused argv) | |
1152 | { | |
1153 | test_ldt64_with_bsdsig(); | |
1154 | test_ldt64_with_machexc(); | |
1155 | } | |
1156 | #endif |