1 : /*
2 : * Copyright (c) 2013 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 "native_client/src/trusted/service_runtime/thread_suspension_unwind.h"
8 :
9 : #include "native_client/src/trusted/cpu_features/arch/x86/cpu_x86.h"
10 : #include "native_client/src/trusted/service_runtime/arch/arm/tramp_arm.h"
11 : #include "native_client/src/trusted/service_runtime/arch/x86_64/tramp_64.h"
12 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
13 : #include "native_client/src/trusted/service_runtime/nacl_config.h"
14 : #include "native_client/src/trusted/service_runtime/nacl_copy.h"
15 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
16 :
17 :
18 5749 : static void GetNaClSyscallSeg(struct NaClApp *nap,
19 5749 : uintptr_t *nacl_syscall_seg,
20 5749 : uintptr_t *nacl_syscall_seg_regs_saved) {
21 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
22 : NaClCPUFeaturesX86 *features = (NaClCPUFeaturesX86 *) nap->cpu_features;
23 : if (NaClGetCPUFeatureX86(features, NaClCPUFeatureX86_SSE)) {
24 : *nacl_syscall_seg = (uintptr_t) &NaClSyscallSegSSE;
25 : *nacl_syscall_seg_regs_saved = (uintptr_t) &NaClSyscallSegRegsSavedSSE;
26 : } else {
27 : *nacl_syscall_seg = (uintptr_t) &NaClSyscallSegNoSSE;
28 : *nacl_syscall_seg_regs_saved = (uintptr_t) &NaClSyscallSegRegsSavedNoSSE;
29 : }
30 : #else
31 11498 : UNREFERENCED_PARAMETER(nap);
32 :
33 5749 : *nacl_syscall_seg = (uintptr_t) &NaClSyscallSeg;
34 5749 : *nacl_syscall_seg_regs_saved = (uintptr_t) &NaClSyscallSegRegsSaved;
35 : #endif
36 5749 : }
37 :
38 : /*
39 : * This returns 0 if |regs| indicates that the thread's register state
40 : * has already been saved in NaClThreadContext. Otherwise, it adjusts
41 : * |regs| to undo the effects of calling the syscall trampoline and
42 : * returns 1.
43 : */
44 5749 : static int Unwind(struct NaClAppThread *natp, struct NaClSignalContext *regs,
45 5749 : enum NaClUnwindCase *unwind_case) {
46 5749 : struct NaClApp *nap = natp->nap;
47 5749 : uintptr_t nacl_syscall_seg;
48 5749 : uintptr_t nacl_syscall_seg_regs_saved;
49 :
50 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
51 : if (regs->cs == natp->user.cs &&
52 : regs->prog_ctr >= nap->syscall_return_springboard.start_addr &&
53 : regs->prog_ctr < nap->syscall_return_springboard.end_addr) {
54 : *unwind_case = NACL_UNWIND_in_springboard;
55 : return 0;
56 : }
57 : if (regs->cs == natp->user.cs &&
58 : regs->prog_ctr >= NACL_TRAMPOLINE_START &&
59 : regs->prog_ctr < NACL_TRAMPOLINE_END) {
60 : *unwind_case = NACL_UNWIND_in_trampoline;
61 : regs->stack_ptr += 4; /* Pop user return address */
62 : return 1;
63 : }
64 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
65 10020 : if (regs->prog_ctr >= NaClUserToSys(nap, NACL_TRAMPOLINE_START) &&
66 4271 : regs->prog_ctr < NaClUserToSys(nap, NACL_TRAMPOLINE_END)) {
67 0 : *unwind_case = NACL_UNWIND_in_trampoline;
68 0 : regs->stack_ptr += 8; /* Pop user return address */
69 0 : return 1;
70 : }
71 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
72 : if (regs->prog_ctr >= NACL_TRAMPOLINE_START &&
73 : regs->prog_ctr < NACL_TRAMPOLINE_END) {
74 : *unwind_case = NACL_UNWIND_in_trampoline;
75 : regs->prog_ctr = NaClSandboxCodeAddr(nap, regs->lr);
76 : return 1;
77 : }
78 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
79 : if (regs->prog_ctr >= NACL_TRAMPOLINE_START &&
80 : regs->prog_ctr < NACL_TRAMPOLINE_END) {
81 : *unwind_case = NACL_UNWIND_in_trampoline;
82 : regs->prog_ctr = NaClSandboxCodeAddr(nap, regs->return_addr);
83 : return 1;
84 : }
85 : #endif
86 :
87 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
88 : if (regs->cs == NaClGetGlobalCs() &&
89 : regs->prog_ctr >= nap->pcrel_thunk &&
90 : regs->prog_ctr < nap->pcrel_thunk_end) {
91 : *unwind_case = NACL_UNWIND_in_pcrel_thunk;
92 : regs->stack_ptr += 4 + 8; /* Pop user + trampoline return addresses */
93 : return 1;
94 : }
95 : #endif
96 :
97 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
98 10020 : if (regs->prog_ctr >= (uintptr_t) &NaClGetTlsFastPath1 &&
99 : regs->prog_ctr < (uintptr_t) &NaClGetTlsFastPath1End) {
100 0 : *unwind_case = NACL_UNWIND_in_tls_fast_path;
101 0 : if (regs->prog_ctr < (uintptr_t) &NaClGetTlsFastPath1RspRestored) {
102 0 : regs->stack_ptr += 8; /* Pop user return address */
103 0 : }
104 0 : return 1;
105 : }
106 10020 : if (regs->prog_ctr >= (uintptr_t) &NaClGetTlsFastPath2 &&
107 : regs->prog_ctr < (uintptr_t) &NaClGetTlsFastPath2End) {
108 0 : *unwind_case = NACL_UNWIND_in_tls_fast_path;
109 0 : if (regs->prog_ctr < (uintptr_t) &NaClGetTlsFastPath2RspRestored) {
110 0 : regs->stack_ptr += 8; /* Pop user return address */
111 0 : }
112 0 : return 1;
113 : }
114 : #endif
115 :
116 5749 : GetNaClSyscallSeg(nap, &nacl_syscall_seg, &nacl_syscall_seg_regs_saved);
117 10020 : if (regs->prog_ctr >= nacl_syscall_seg &&
118 : regs->prog_ctr < nacl_syscall_seg_regs_saved) {
119 0 : *unwind_case = NACL_UNWIND_in_syscallseg;
120 : #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 32
121 : /* Pop user + trampoline return addresses */
122 : regs->stack_ptr += 4 + 8;
123 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64
124 : /* Pop user return address. */
125 0 : regs->stack_ptr += 8;
126 : #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
127 : /*
128 : * On MIPS, $ra is not modified from the start of the trampoline to the
129 : * point where registers are saved.
130 : */
131 : regs->prog_ctr = NaClSandboxCodeAddr(nap, regs->return_addr);
132 : #endif
133 0 : return 1;
134 : }
135 :
136 5749 : *unwind_case = NACL_UNWIND_after_saving_regs;
137 5749 : return 0;
138 5749 : }
139 :
140 : /*
141 : * Given that thread |natp| has been suspended during a
142 : * trusted/untrusted context switch and has trusted register state
143 : * |regs|, this modifies |regs| to contain untrusted register state
144 : * (that is, the state the syscall will return with).
145 : */
146 5749 : void NaClGetRegistersForContextSwitch(struct NaClAppThread *natp,
147 5749 : struct NaClSignalContext *regs,
148 5749 : enum NaClUnwindCase *unwind_case) {
149 5749 : if (Unwind(natp, regs, unwind_case)) {
150 0 : NaClSignalContextUnsetClobberedRegisters(regs);
151 0 : } else {
152 5749 : NaClThreadContextToSignalContext(&natp->user, regs);
153 : }
154 :
155 : if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 ||
156 : (NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm &&
157 : *unwind_case != NACL_UNWIND_in_trampoline) ||
158 : (NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips &&
159 : *unwind_case != NACL_UNWIND_in_trampoline &&
160 : *unwind_case != NACL_UNWIND_in_syscallseg)) {
161 : /*
162 : * Read the return address from the untrusted stack.
163 : * NaClCopyInFromUser() can fault or return an error here, but only
164 : * if the thread was suspended just before it was about to crash.
165 : * This can happen if untrusted code JMP'd to the trampoline with a
166 : * bad stack pointer or if the thread was racing with an munmap().
167 : */
168 5749 : nacl_reg_t user_ret = 0;
169 5749 : if (!NaClCopyInFromUser(natp->nap, &user_ret,
170 : (uint32_t) (regs->stack_ptr + NACL_USERRET_FIX),
171 : sizeof(user_ret))) {
172 0 : NaClLog(LOG_WARNING, "NaClGetRegistersForContextSwitch: "
173 : "Failed to read return address; using dummy value\n");
174 0 : }
175 5749 : regs->prog_ctr = NaClSandboxCodeAddr(natp->nap, user_ret);
176 : }
177 5749 : }
|