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.
7 #include "native_client/src/include/nacl_assert.h"
8 #include "native_client/src/include/nacl_macros.h"
9 #include "native_client/src/include/portability_io.h"
10 #include "native_client/src/shared/platform/nacl_check.h"
11 #include "native_client/src/shared/platform/nacl_exit.h"
12 #include "native_client/src/shared/platform/nacl_log.h"
13 #include "native_client/src/shared/platform/nacl_sync_checked.h"
14 #include "native_client/src/trusted/service_runtime/arch/sel_ldr_arch.h"
15 #include "native_client/src/trusted/service_runtime/include/bits/mman.h"
16 #include "native_client/src/trusted/service_runtime/load_file.h"
17 #include "native_client/src/trusted/service_runtime/nacl_all_modules.h"
18 #include "native_client/src/trusted/service_runtime/nacl_app.h"
19 #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
20 #include "native_client/src/trusted/service_runtime/nacl_copy.h"
21 #include "native_client/src/trusted/service_runtime/nacl_signal.h"
22 #include "native_client/src/trusted/service_runtime/osx/mach_exception_handler.h"
23 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
24 #include "native_client/src/trusted/service_runtime/sys_memory.h"
25 #include "native_client/src/trusted/service_runtime/thread_suspension.h"
26 #include "native_client/src/trusted/service_runtime/win/debug_exception_handler.h"
27 #include "native_client/tests/common/register_set.h"
31 * This test program checks that we get notification of a fault that
32 * happens in untrusted code in faultqueue_test_guest.c.
35 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
36 static const int kBreakInstructionSize = 1;
37 static const int kBreakInstructionSignal = NACL_ABI_SIGSEGV;
38 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
39 static const int kBreakInstructionSize = 4;
40 static const int kBreakInstructionSignal = NACL_ABI_SIGILL;
41 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
42 static const int kBreakInstructionSize = 4;
43 static const int kBreakInstructionSignal = NACL_ABI_SIGTRAP;
45 # error Unsupported architecture
48 void WaitForThreadToFault(struct NaClApp *nap) {
50 * Wait until a faulted thread is reported.
51 * TODO(mseaborn): We should not busy-wait here.
52 * See http://code.google.com/p/nativeclient/issues/detail?id=2952
54 while (*(volatile Atomic32 *) &nap->faulted_thread_count == 0) {
57 ASSERT_EQ(nap->faulted_thread_count, 1);
60 /* This must be called with the mutex nap->threads_mu held. */
61 struct NaClAppThread *GetOnlyThread(struct NaClApp *nap) {
62 struct NaClAppThread *found_thread = NULL;
64 for (index = 0; index < nap->threads.num_entries; index++) {
65 struct NaClAppThread *natp = NaClGetThreadMu(nap, (int) index);
67 CHECK(found_thread == NULL);
71 CHECK(found_thread != NULL);
76 * This spins until any previous NaClAppThread has exited to the point
77 * where it is removed from the thread array, so that it will not be
78 * encountered by a subsequent call to GetOnlyThread(). This is
79 * necessary because the threads hosting NaClAppThreads are unjoined.
81 static void WaitForThreadToExitFully(struct NaClApp *nap) {
84 NaClXMutexLock(&nap->threads_mu);
85 done = (nap->num_threads == 0);
86 NaClXMutexUnlock(&nap->threads_mu);
90 struct NaClSignalContext *StartGuestWithSharedMemory(
91 struct NaClApp *nap) {
93 char *args[] = {"prog_name", arg_string};
95 struct NaClSignalContext *expected_regs;
98 * Allocate some space in untrusted address space. We pass the
99 * address to the guest program so that we can share data with it.
101 mmap_addr = NaClSysMmapIntern(
102 nap, NULL, sizeof(*expected_regs),
103 NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
104 NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
106 SNPRINTF(arg_string, sizeof(arg_string), "0x%x", (unsigned int) mmap_addr);
107 expected_regs = (struct NaClSignalContext *) NaClUserToSys(nap, mmap_addr);
109 WaitForThreadToExitFully(nap);
111 CHECK(NaClCreateMainThread(nap, NACL_ARRAY_SIZE(args), args, NULL));
112 return expected_regs;
115 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
118 * This function is entered with threads suspended, and it exits with
119 * threads suspended too.
121 void TestSingleStepping(struct NaClAppThread *natp) {
122 struct NaClApp *nap = natp->nap;
123 struct NaClSignalContext regs;
124 struct NaClSignalContext expected_regs;
127 /* Set the trap flag to enable single-stepping. */
128 NaClAppThreadGetSuspendedRegisters(natp, ®s);
129 regs.flags |= NACL_X86_TRAP_FLAG;
130 expected_regs = regs;
131 NaClAppThreadSetSuspendedRegisters(natp, ®s);
133 for (instr_size = 1; instr_size <= 5; instr_size++) {
136 fprintf(stderr, "Now expecting to single-step through "
137 "an instruction %i bytes in size at address 0x%x\n",
138 instr_size, (int) expected_regs.prog_ctr);
139 expected_regs.prog_ctr += instr_size;
141 NaClUntrustedThreadsResumeAll(nap);
142 WaitForThreadToFault(nap);
143 NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
144 ASSERT_EQ(GetOnlyThread(nap), natp);
145 ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1);
147 * TODO(mseaborn): Move ExceptionToSignal() out of debug_stub and
148 * enable this check on Windows too.
151 ASSERT_EQ(signal, NACL_ABI_SIGTRAP);
154 NaClAppThreadGetSuspendedRegisters(natp, ®s);
157 * On Windows, but not elsewhere, the trap flag gets unset after
158 * each instruction, so we must set it again.
160 regs.flags |= NACL_X86_TRAP_FLAG;
161 NaClAppThreadSetSuspendedRegisters(natp, ®s);
163 RegsAssertEqual(®s, &expected_regs);
166 /* Unset the trap flag so that the thread can continue. */
167 regs.flags &= ~NACL_X86_TRAP_FLAG;
168 NaClAppThreadSetSuspendedRegisters(natp, ®s);
171 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm || \
172 NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
174 /* ARM/MIPS do not have hardware single-stepping, so nothing to do here. */
175 void TestSingleStepping(struct NaClAppThread *natp) {
176 UNREFERENCED_PARAMETER(natp);
180 # error Unsupported architecture
183 void TestReceivingFault(struct NaClApp *nap) {
184 struct NaClSignalContext *expected_regs = StartGuestWithSharedMemory(nap);
185 struct NaClSignalContext regs;
186 struct NaClAppThread *natp;
190 WaitForThreadToFault(nap);
191 NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
192 natp = GetOnlyThread(nap);
193 ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1);
195 * TODO(mseaborn): Move ExceptionToSignal() out of debug_stub and
196 * enable this check on Windows too.
199 ASSERT_EQ(signal, kBreakInstructionSignal);
202 /* Check that faulted_thread_count is updated correctly. */
203 ASSERT_EQ(nap->faulted_thread_count, 0);
205 * Check that NaClAppThreadUnblockIfFaulted() returns false when
206 * called on a thread that is not blocked.
208 ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &dummy_signal), 0);
210 NaClAppThreadGetSuspendedRegisters(natp, ®s);
211 RegsAssertEqual(®s, expected_regs);
213 /* Skip over the instruction that faulted. */
214 regs.prog_ctr += kBreakInstructionSize;
215 NaClAppThreadSetSuspendedRegisters(natp, ®s);
217 TestSingleStepping(natp);
219 NaClUntrustedThreadsResumeAll(nap);
220 CHECK(NaClWaitForMainThreadToExit(nap) == 0);
224 * This is a test for a bug that occurred only on Mac OS X on x86-32.
225 * If we modify the registers of a suspended thread on x86-32 Mac, we
226 * force the thread to return to untrusted code via the special
227 * trusted routine NaClSwitchRemainingRegsViaECX(). If we later
228 * suspend the thread while it is still executing this routine, we
229 * need to return the untrusted register state that the routine is
230 * attempting to restore -- we test that here.
232 * Suspending a thread while it is still blocked by a fault is the
233 * easiest way to test this, since this allows us to test suspension
234 * while the thread is at the start of
235 * NaClSwitchRemainingRegsViaECX(). This is also how the debug stub
236 * runs into this problem.
238 void TestGettingRegistersInMacSwitchRemainingRegs(struct NaClApp *nap) {
239 struct NaClSignalContext *expected_regs = StartGuestWithSharedMemory(nap);
240 struct NaClSignalContext regs;
241 struct NaClAppThread *natp;
244 WaitForThreadToFault(nap);
245 NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
246 natp = GetOnlyThread(nap);
247 NaClAppThreadGetSuspendedRegisters(natp, ®s);
248 RegsAssertEqual(®s, expected_regs);
250 * On Mac OS X on x86-32, this changes the underlying Mac thread's
251 * state so that the thread will execute
252 * NaClSwitchRemainingRegsViaECX().
254 NaClAppThreadSetSuspendedRegisters(natp, ®s);
257 * Resume the thread without unblocking it and then re-suspend it.
258 * This causes osx/thread_suspension.c to re-fetch the register
259 * state from the Mac kernel.
261 NaClUntrustedThreadsResumeAll(nap);
262 NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
263 ASSERT_EQ(GetOnlyThread(nap), natp);
265 /* We should get the same register state as before. */
266 NaClAppThreadGetSuspendedRegisters(natp, ®s);
267 RegsAssertEqual(®s, expected_regs);
270 * Clean up: Skip over the instruction that faulted and let the
271 * thread run to completion.
273 regs.prog_ctr += kBreakInstructionSize;
274 NaClAppThreadSetSuspendedRegisters(natp, ®s);
275 ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1);
276 NaClUntrustedThreadsResumeAll(nap);
277 CHECK(NaClWaitForMainThreadToExit(nap) == 0);
280 int main(int argc, char **argv) {
282 struct NaClApp *nap = &app;
284 NaClDebugExceptionHandlerStandaloneHandleArgs(argc, argv);
285 NaClHandleBootstrapArgs(&argc, &argv);
287 /* Turn off buffering to aid debugging. */
288 setvbuf(stdout, NULL, _IONBF, 0);
289 setvbuf(stderr, NULL, _IONBF, 0);
291 NaClAllModulesInit();
294 NaClLog(LOG_FATAL, "Expected 1 argument: executable filename\n");
297 CHECK(NaClAppCtor(nap));
298 CHECK(NaClAppLoadFileFromFilename(nap, argv[1]) == LOAD_OK);
299 NaClAppInitialDescriptorHookup(nap);
302 NaClSignalHandlerInit();
304 CHECK(NaClInterceptMachExceptions());
306 nap->attach_debug_exception_handler_func =
307 NaClDebugExceptionHandlerStandaloneAttach;
309 # error Unknown host OS
311 CHECK(NaClFaultedThreadQueueEnable(nap));
313 printf("Running TestReceivingFault...\n");
314 TestReceivingFault(nap);
316 printf("Running TestGettingRegistersInMacSwitchRemainingRegs...\n");
317 TestGettingRegistersInMacSwitchRemainingRegs(nap);
320 * Avoid calling exit() because it runs process-global destructors
321 * which might break code that is running in our unjoined threads.