]> git.saurik.com Git - apple/xnu.git/blame - tests/ldt.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / tests / ldt.c
CommitLineData
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
55T_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)
151typedef struct {
152 uint64_t off;
153 uint16_t seg;
154} far_call_t;
155#pragma pack()
156
157typedef struct {
158 uint64_t stack_base;
159 uint64_t stack_limit;
160 uint64_t GSbase;
161} stackaddr_to_gsbase_t;
162
163typedef 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
170typedef struct custom_tsd {
171 struct custom_tsd * this_tsd_base;
172 uint64_t orig_tsd_base;
173} custom_tsd_t;
174
175typedef 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)))
179static custom_tsd_t GS_RELATIVE *mytsd = (custom_tsd_t GS_RELATIVE *)0;
180
181static far_call_t input_desc = { .seg = COMPAT_MODE_CS_SELECTOR, .off = 0 };
182static uint64_t stackAddr = 0;
183static compat_tramp_t thunkit = NULL;
184static uint64_t thunk64_addr;
cb323159
A
185/* stack2gs[0] is initialized in map_lowmem_stack() */
186static stackaddr_to_gsbase_t stack2gs[] = { { 0 } };
0a7de745
A
187
188extern int compat_mode_trampoline(far_call_t *, void *, uint64_t);
189extern void long_mode_trampoline(void);
190extern boolean_t mach_exc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP);
191
192extern void code_32(void);
193
194kern_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
206kern_return_t
207catch_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
217kern_return_t
218catch_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
230extern void _thread_set_tsd_base(uint64_t);
231static uint64_t stack_range_to_GSbase(uint64_t stackptr, uint64_t GSbase);
232void restore_gsbase(uint64_t stackptr);
233
234static uint64_t
235get_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
250void
251restore_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 */
265kern_return_t
266catch_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
283kern_return_t
284catch_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
299static void
300handle_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
310static void
311handle_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
348kern_return_t
349catch_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
416static void *
417handle_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
430static void
431init_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
469static union ldt_entry *descs = 0;
470static uint64_t idx;
471static int saw_ud2 = 0;
472static boolean_t ENV_set_ldt_in_sighandler = FALSE;
473
474static void
475signal_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
603static void
604setup_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
636static void
637teardown_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
659static void
660dump_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
673static int
674map_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
710static int
cb323159 711map_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
760static int
761map_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
770static uint64_t
771stack_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
791static uint64_t
792call_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
832static uint64_t
833get_cursp(void)
834{
835 uint64_t curstk;
836 __asm__ __volatile__ ("movq %%rsp, %0" : "=r" (curstk) :: "memory");
837 return curstk;
838}
839
840static void
841hello_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 */
854static void *
855thread_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
894static void
895join_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
905static int
906create_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
916static void
917ldt64_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
1061static void
1062test_ldt64_with_bsdsig(void)
1063#else
1064/*
1065 * Main test declarations
1066 */
1067T_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
1108static void
1109test_ldt64_with_machexc(void)
1110#else
1111T_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
1150int
1151main(int __unused argc, char ** __unused argv)
1152{
1153 test_ldt64_with_bsdsig();
1154 test_ldt64_with_machexc();
1155}
1156#endif