Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / native_client / tests / faulted_thread_queue / faultqueue_test_host.c
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 "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"
28
29
30 /*
31  * This test program checks that we get notification of a fault that
32  * happens in untrusted code in faultqueue_test_guest.c.
33  */
34
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;
44 #else
45 # error Unsupported architecture
46 #endif
47
48 void WaitForThreadToFault(struct NaClApp *nap) {
49   /*
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
53    */
54   while (*(volatile Atomic32 *) &nap->faulted_thread_count == 0) {
55     /* Do nothing. */
56   }
57   ASSERT_EQ(nap->faulted_thread_count, 1);
58 }
59
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;
63   size_t index;
64   for (index = 0; index < nap->threads.num_entries; index++) {
65     struct NaClAppThread *natp = NaClGetThreadMu(nap, (int) index);
66     if (natp != NULL) {
67       CHECK(found_thread == NULL);
68       found_thread = natp;
69     }
70   }
71   CHECK(found_thread != NULL);
72   return found_thread;
73 }
74
75 /*
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.
80  */
81 static void WaitForThreadToExitFully(struct NaClApp *nap) {
82   int done;
83   do {
84     NaClXMutexLock(&nap->threads_mu);
85     done = (nap->num_threads == 0);
86     NaClXMutexUnlock(&nap->threads_mu);
87   } while (!done);
88 }
89
90 struct NaClSignalContext *StartGuestWithSharedMemory(
91     struct NaClApp *nap) {
92   char arg_string[32];
93   char *args[] = {"prog_name", arg_string};
94   uint32_t mmap_addr;
95   struct NaClSignalContext *expected_regs;
96
97   /*
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.
100    */
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,
105       -1, 0);
106   SNPRINTF(arg_string, sizeof(arg_string), "0x%x", (unsigned int) mmap_addr);
107   expected_regs = (struct NaClSignalContext *) NaClUserToSys(nap, mmap_addr);
108
109   WaitForThreadToExitFully(nap);
110
111   CHECK(NaClCreateMainThread(nap, NACL_ARRAY_SIZE(args), args, NULL));
112   return expected_regs;
113 }
114
115 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
116
117 /*
118  * This function is entered with threads suspended, and it exits with
119  * threads suspended too.
120  */
121 void TestSingleStepping(struct NaClAppThread *natp) {
122   struct NaClApp *nap = natp->nap;
123   struct NaClSignalContext regs;
124   struct NaClSignalContext expected_regs;
125   int instr_size;
126
127   /* Set the trap flag to enable single-stepping. */
128   NaClAppThreadGetSuspendedRegisters(natp, &regs);
129   regs.flags |= NACL_X86_TRAP_FLAG;
130   expected_regs = regs;
131   NaClAppThreadSetSuspendedRegisters(natp, &regs);
132
133   for (instr_size = 1; instr_size <= 5; instr_size++) {
134     int signal;
135
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;
140
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);
146     /*
147      * TODO(mseaborn): Move ExceptionToSignal() out of debug_stub and
148      * enable this check on Windows too.
149      */
150     if (!NACL_WINDOWS) {
151       ASSERT_EQ(signal, NACL_ABI_SIGTRAP);
152     }
153
154     NaClAppThreadGetSuspendedRegisters(natp, &regs);
155     if (NACL_WINDOWS) {
156       /*
157        * On Windows, but not elsewhere, the trap flag gets unset after
158        * each instruction, so we must set it again.
159        */
160       regs.flags |= NACL_X86_TRAP_FLAG;
161       NaClAppThreadSetSuspendedRegisters(natp, &regs);
162     }
163     RegsAssertEqual(&regs, &expected_regs);
164   }
165
166   /* Unset the trap flag so that the thread can continue. */
167   regs.flags &= ~NACL_X86_TRAP_FLAG;
168   NaClAppThreadSetSuspendedRegisters(natp, &regs);
169 }
170
171 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm || \
172       NACL_ARCH(NACL_BUILD_ARCH) == NACL_mips
173
174 /* ARM/MIPS do not have hardware single-stepping, so nothing to do here. */
175 void TestSingleStepping(struct NaClAppThread *natp) {
176   UNREFERENCED_PARAMETER(natp);
177 }
178
179 #else
180 # error Unsupported architecture
181 #endif
182
183 void TestReceivingFault(struct NaClApp *nap) {
184   struct NaClSignalContext *expected_regs = StartGuestWithSharedMemory(nap);
185   struct NaClSignalContext regs;
186   struct NaClAppThread *natp;
187   int signal = 0;
188   int dummy_signal;
189
190   WaitForThreadToFault(nap);
191   NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
192   natp = GetOnlyThread(nap);
193   ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1);
194   /*
195    * TODO(mseaborn): Move ExceptionToSignal() out of debug_stub and
196    * enable this check on Windows too.
197    */
198   if (!NACL_WINDOWS) {
199     ASSERT_EQ(signal, kBreakInstructionSignal);
200   }
201
202   /* Check that faulted_thread_count is updated correctly. */
203   ASSERT_EQ(nap->faulted_thread_count, 0);
204   /*
205    * Check that NaClAppThreadUnblockIfFaulted() returns false when
206    * called on a thread that is not blocked.
207    */
208   ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &dummy_signal), 0);
209
210   NaClAppThreadGetSuspendedRegisters(natp, &regs);
211   RegsAssertEqual(&regs, expected_regs);
212
213   /* Skip over the instruction that faulted. */
214   regs.prog_ctr += kBreakInstructionSize;
215   NaClAppThreadSetSuspendedRegisters(natp, &regs);
216
217   TestSingleStepping(natp);
218
219   NaClUntrustedThreadsResumeAll(nap);
220   CHECK(NaClWaitForMainThreadToExit(nap) == 0);
221 }
222
223 /*
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.
231  *
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.
237  */
238 void TestGettingRegistersInMacSwitchRemainingRegs(struct NaClApp *nap) {
239   struct NaClSignalContext *expected_regs = StartGuestWithSharedMemory(nap);
240   struct NaClSignalContext regs;
241   struct NaClAppThread *natp;
242   int signal;
243
244   WaitForThreadToFault(nap);
245   NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
246   natp = GetOnlyThread(nap);
247   NaClAppThreadGetSuspendedRegisters(natp, &regs);
248   RegsAssertEqual(&regs, expected_regs);
249   /*
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().
253    */
254   NaClAppThreadSetSuspendedRegisters(natp, &regs);
255
256   /*
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.
260    */
261   NaClUntrustedThreadsResumeAll(nap);
262   NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
263   ASSERT_EQ(GetOnlyThread(nap), natp);
264
265   /* We should get the same register state as before. */
266   NaClAppThreadGetSuspendedRegisters(natp, &regs);
267   RegsAssertEqual(&regs, expected_regs);
268
269   /*
270    * Clean up:  Skip over the instruction that faulted and let the
271    * thread run to completion.
272    */
273   regs.prog_ctr += kBreakInstructionSize;
274   NaClAppThreadSetSuspendedRegisters(natp, &regs);
275   ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1);
276   NaClUntrustedThreadsResumeAll(nap);
277   CHECK(NaClWaitForMainThreadToExit(nap) == 0);
278 }
279
280 int main(int argc, char **argv) {
281   struct NaClApp app;
282   struct NaClApp *nap = &app;
283
284   NaClDebugExceptionHandlerStandaloneHandleArgs(argc, argv);
285   NaClHandleBootstrapArgs(&argc, &argv);
286
287   /* Turn off buffering to aid debugging. */
288   setvbuf(stdout, NULL, _IONBF, 0);
289   setvbuf(stderr, NULL, _IONBF, 0);
290
291   NaClAllModulesInit();
292
293   if (argc != 2) {
294     NaClLog(LOG_FATAL, "Expected 1 argument: executable filename\n");
295   }
296
297   CHECK(NaClAppCtor(nap));
298   CHECK(NaClAppLoadFileFromFilename(nap, argv[1]) == LOAD_OK);
299   NaClAppInitialDescriptorHookup(nap);
300
301 #if NACL_LINUX
302   NaClSignalHandlerInit();
303 #elif NACL_OSX
304   CHECK(NaClInterceptMachExceptions());
305 #elif NACL_WINDOWS
306   nap->attach_debug_exception_handler_func =
307       NaClDebugExceptionHandlerStandaloneAttach;
308 #else
309 # error Unknown host OS
310 #endif
311   CHECK(NaClFaultedThreadQueueEnable(nap));
312
313   printf("Running TestReceivingFault...\n");
314   TestReceivingFault(nap);
315
316   printf("Running TestGettingRegistersInMacSwitchRemainingRegs...\n");
317   TestGettingRegistersInMacSwitchRemainingRegs(nap);
318
319   /*
320    * Avoid calling exit() because it runs process-global destructors
321    * which might break code that is running in our unjoined threads.
322    */
323   NaClExit(0);
324 }