1 : /*
2 : * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include <mach/mach.h>
8 : #include <mach/thread_status.h>
9 :
10 : #include "native_client/src/shared/platform/nacl_check.h"
11 : #include "native_client/src/shared/platform/nacl_sync_checked.h"
12 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
13 : #include "native_client/src/trusted/service_runtime/nacl_switch_to_app.h"
14 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
15 : #include "native_client/src/trusted/service_runtime/thread_suspension.h"
16 :
17 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
18 : # include "native_client/src/trusted/service_runtime/arch/x86_32/nacl_switch_all_regs_32.h"
19 : #endif
20 :
21 :
22 : struct NaClAppThreadSuspendedRegisters {
23 : x86_thread_state_t context;
24 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
25 : struct NaClSwitchRemainingRegsState switch_state;
26 : #endif
27 : };
28 :
29 12326 : static mach_port_t GetHostThreadPort(struct NaClAppThread *natp) {
30 36978 : CHECK(natp->host_thread_is_defined);
31 12326 : return pthread_mach_thread_np(natp->host_thread.tid);
32 : }
33 :
34 7008361 : void NaClAppThreadSetSuspendState(struct NaClAppThread *natp,
35 7008361 : enum NaClSuspendState old_state,
36 7008361 : enum NaClSuspendState new_state) {
37 : /*
38 : * Claiming suspend_mu here blocks a trusted/untrusted context
39 : * switch while the thread is suspended or a suspension is in
40 : * progress.
41 : */
42 7008361 : NaClXMutexLock(&natp->suspend_mu);
43 28033430 : DCHECK(natp->suspend_state == (Atomic32) old_state);
44 7008356 : natp->suspend_state = new_state;
45 7008356 : NaClXMutexUnlock(&natp->suspend_mu);
46 7008356 : }
47 :
48 12404 : void NaClUntrustedThreadSuspend(struct NaClAppThread *natp,
49 12404 : int save_registers) {
50 : /*
51 : * We claim suspend_mu here to block trusted/untrusted context
52 : * switches by blocking NaClAppThreadSetSuspendState(). This blocks
53 : * any untrusted->trusted context switch that might happen before
54 : * SuspendThread() takes effect. It blocks any trusted->untrusted
55 : * context switch that might happen if the syscall running in the
56 : * target thread returns.
57 : */
58 12404 : NaClXMutexLock(&natp->suspend_mu);
59 12404 : if (natp->suspend_state == NACL_APP_THREAD_UNTRUSTED) {
60 6086 : kern_return_t result;
61 6086 : mach_msg_type_number_t size;
62 6086 : mach_port_t thread_port = GetHostThreadPort(natp);
63 :
64 6086 : result = thread_suspend(thread_port);
65 6086 : if (result != KERN_SUCCESS) {
66 0 : NaClLog(LOG_FATAL, "NaClUntrustedThreadSuspend: "
67 : "thread_suspend() call failed: error %d\n", (int) result);
68 0 : }
69 :
70 6086 : if (save_registers) {
71 5845 : if (natp->suspended_registers == NULL) {
72 25 : natp->suspended_registers = malloc(sizeof(*natp->suspended_registers));
73 25 : if (natp->suspended_registers == NULL) {
74 0 : NaClLog(LOG_FATAL, "NaClUntrustedThreadSuspend: malloc() failed\n");
75 0 : }
76 25 : }
77 :
78 5845 : size = sizeof(natp->suspended_registers->context) / sizeof(natural_t);
79 5845 : result = thread_get_state(thread_port, x86_THREAD_STATE,
80 : (void *) &natp->suspended_registers->context,
81 : &size);
82 5845 : if (result != KERN_SUCCESS) {
83 0 : NaClLog(LOG_FATAL, "NaClUntrustedThreadSuspend: "
84 : "thread_get_state() call failed: error %d\n", (int) result);
85 0 : }
86 5845 : }
87 6086 : }
88 : /*
89 : * We leave suspend_mu held so that NaClAppThreadSetSuspendState()
90 : * will block.
91 : */
92 12404 : }
93 :
94 12387 : void NaClUntrustedThreadResume(struct NaClAppThread *natp) {
95 12387 : if (natp->suspend_state == NACL_APP_THREAD_UNTRUSTED) {
96 6071 : kern_return_t result = thread_resume(GetHostThreadPort(natp));
97 6071 : if (result != KERN_SUCCESS) {
98 0 : NaClLog(LOG_FATAL, "NaClUntrustedThreadResume: "
99 : "thread_resume() call failed: error %d\n", (int) result);
100 0 : }
101 6071 : }
102 12387 : NaClXMutexUnlock(&natp->suspend_mu);
103 12387 : }
104 :
105 : void NaClAppThreadGetSuspendedRegistersInternal(
106 11674 : struct NaClAppThread *natp, struct NaClSignalContext *regs) {
107 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
108 : /*
109 : * We might have suspended the thread while it is returning to
110 : * untrusted code via NaClSwitchRemainingRegsViaECX() and
111 : * NaCl_springboard_all_regs. This is particularly likely for a
112 : * faulted thread that has been resumed and suspended again without
113 : * ever being unblocked by NaClAppThreadUnblockIfFaulted().
114 : *
115 : * In this situation, we must undo the register state modifications
116 : * made by NaClAppThreadSetSuspendedRegistersInternal().
117 : */
118 : struct NaClAppThreadSuspendedRegisters *state = natp->suspended_registers;
119 : struct NaClApp *nap = natp->nap;
120 : uint32_t eip = state->context.uts.ts32.__eip;
121 : if ((state->context.uts.ts32.__cs == NaClGetGlobalCs() &&
122 : eip >= (uintptr_t) NaClSwitchRemainingRegsViaECX &&
123 : eip < (uintptr_t) NaClSwitchRemainingRegsAsmEnd) ||
124 : (state->context.uts.ts32.__cs == natp->user.cs &&
125 : eip >= nap->all_regs_springboard.start_addr &&
126 : eip < nap->all_regs_springboard.end_addr)) {
127 : state->context.uts.ts32.__eip = natp->user.gs_segment.new_prog_ctr;
128 : state->context.uts.ts32.__ecx = natp->user.gs_segment.new_ecx;
129 : /*
130 : * It is sometimes necessary to restore the following registers
131 : * too, depending on how far we are through
132 : * NaClSwitchRemainingRegsViaECX().
133 : */
134 : state->context.uts.ts32.__cs = natp->user.cs;
135 : state->context.uts.ts32.__ds = natp->user.ds;
136 : state->context.uts.ts32.__es = natp->user.es;
137 : state->context.uts.ts32.__fs = natp->user.fs;
138 : state->context.uts.ts32.__gs = natp->user.gs;
139 : state->context.uts.ts32.__ss = natp->user.ss;
140 : }
141 : #endif
142 :
143 11674 : NaClSignalContextFromMacThreadState(regs,
144 : &natp->suspended_registers->context);
145 11674 : }
146 :
147 : void NaClAppThreadSetSuspendedRegistersInternal(
148 78 : struct NaClAppThread *natp, const struct NaClSignalContext *regs) {
149 78 : kern_return_t result;
150 78 : mach_msg_type_number_t size;
151 78 : struct NaClAppThreadSuspendedRegisters *state = natp->suspended_registers;
152 78 : x86_thread_state_t context_copy;
153 :
154 78 : NaClSignalContextToMacThreadState(&state->context, regs);
155 78 : context_copy = state->context;
156 :
157 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
158 : /*
159 : * thread_set_state() ignores the %cs value we supply and always
160 : * resets %cs back to the trusted-code value. This means we must
161 : * set up the new untrusted register state via a trusted code
162 : * routine which returns to untrusted code via a springboard.
163 : *
164 : * We reset %cs here in case the Mac kernel is ever fixed to not
165 : * ignore the supplied %cs value.
166 : */
167 : context_copy.uts.ts32.__cs = NaClGetGlobalCs();
168 : context_copy.uts.ts32.__ds = NaClGetGlobalDs();
169 : /* Reset these too just in case. */
170 : context_copy.uts.ts32.__es = NaClGetGlobalDs();
171 : context_copy.uts.ts32.__ss = NaClGetGlobalDs();
172 : context_copy.uts.ts32.__ecx = (uintptr_t) &state->switch_state;
173 : context_copy.uts.ts32.__eip = (uintptr_t) NaClSwitchRemainingRegsViaECX;
174 : NaClSwitchRemainingRegsSetup(&state->switch_state, natp, regs);
175 : #endif
176 :
177 78 : size = sizeof(context_copy) / sizeof(natural_t);
178 78 : result = thread_set_state(GetHostThreadPort(natp), x86_THREAD_STATE,
179 : (void *) &context_copy, size);
180 78 : if (result != KERN_SUCCESS) {
181 0 : NaClLog(LOG_FATAL, "NaClAppThreadSetSuspendedRegistersInternal: "
182 : "thread_set_state() call failed: error %d\n", result);
183 0 : }
184 78 : }
185 :
186 92 : int NaClAppThreadUnblockIfFaulted(struct NaClAppThread *natp, int *signal) {
187 92 : kern_return_t result;
188 92 : if (natp->fault_signal == 0) {
189 1 : return 0;
190 : }
191 91 : *signal = natp->fault_signal;
192 91 : natp->fault_signal = 0;
193 91 : AtomicIncrement(&natp->nap->faulted_thread_count, -1);
194 : /*
195 : * Decrement the kernel's suspension count for the thread. This
196 : * undoes the effect of mach_exception_handler.c's thread_suspend()
197 : * call.
198 : */
199 91 : result = thread_resume(GetHostThreadPort(natp));
200 91 : if (result != KERN_SUCCESS) {
201 0 : NaClLog(LOG_FATAL, "NaClAppThreadUnblockIfFaulted: "
202 : "thread_resume() call failed: error %d\n", (int) result);
203 0 : }
204 91 : return 1;
205 92 : }
|