Upstream version 5.34.104.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 #else
42 # error Unsupported architecture
43 #endif
44
45 void WaitForThreadToFault(struct NaClApp *nap) {
46   /*
47    * Wait until a faulted thread is reported.
48    * TODO(mseaborn): We should not busy-wait here.
49    * See http://code.google.com/p/nativeclient/issues/detail?id=2952
50    */
51   while (*(volatile Atomic32 *) &nap->faulted_thread_count == 0) {
52     /* Do nothing. */
53   }
54   ASSERT_EQ(nap->faulted_thread_count, 1);
55 }
56
57 /* This must be called with the mutex nap->threads_mu held. */
58 struct NaClAppThread *GetOnlyThread(struct NaClApp *nap) {
59   struct NaClAppThread *found_thread = NULL;
60   size_t index;
61   for (index = 0; index < nap->threads.num_entries; index++) {
62     struct NaClAppThread *natp = NaClGetThreadMu(nap, (int) index);
63     if (natp != NULL) {
64       CHECK(found_thread == NULL);
65       found_thread = natp;
66     }
67   }
68   CHECK(found_thread != NULL);
69   return found_thread;
70 }
71
72 /*
73  * This spins until any previous NaClAppThread has exited to the point
74  * where it is removed from the thread array, so that it will not be
75  * encountered by a subsequent call to GetOnlyThread().  This is
76  * necessary because the threads hosting NaClAppThreads are unjoined.
77  */
78 static void WaitForThreadToExitFully(struct NaClApp *nap) {
79   int done;
80   do {
81     NaClXMutexLock(&nap->threads_mu);
82     done = (nap->num_threads == 0);
83     NaClXMutexUnlock(&nap->threads_mu);
84   } while (!done);
85 }
86
87 struct NaClSignalContext *StartGuestWithSharedMemory(
88     struct NaClApp *nap) {
89   char arg_string[32];
90   char *args[] = {"prog_name", arg_string};
91   uint32_t mmap_addr;
92   struct NaClSignalContext *expected_regs;
93
94   /*
95    * Allocate some space in untrusted address space.  We pass the
96    * address to the guest program so that we can share data with it.
97    */
98   mmap_addr = NaClSysMmapIntern(
99       nap, NULL, sizeof(*expected_regs),
100       NACL_ABI_PROT_READ | NACL_ABI_PROT_WRITE,
101       NACL_ABI_MAP_PRIVATE | NACL_ABI_MAP_ANONYMOUS,
102       -1, 0);
103   SNPRINTF(arg_string, sizeof(arg_string), "0x%x", (unsigned int) mmap_addr);
104   expected_regs = (struct NaClSignalContext *) NaClUserToSys(nap, mmap_addr);
105
106   WaitForThreadToExitFully(nap);
107
108   CHECK(NaClCreateMainThread(nap, NACL_ARRAY_SIZE(args), args, NULL));
109   return expected_regs;
110 }
111
112 #if NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86
113
114 /*
115  * This function is entered with threads suspended, and it exits with
116  * threads suspended too.
117  */
118 void TestSingleStepping(struct NaClAppThread *natp) {
119   struct NaClApp *nap = natp->nap;
120   struct NaClSignalContext regs;
121   struct NaClSignalContext expected_regs;
122   int instr_size;
123
124   /* Set the trap flag to enable single-stepping. */
125   NaClAppThreadGetSuspendedRegisters(natp, &regs);
126   regs.flags |= NACL_X86_TRAP_FLAG;
127   expected_regs = regs;
128   NaClAppThreadSetSuspendedRegisters(natp, &regs);
129
130   for (instr_size = 1; instr_size <= 5; instr_size++) {
131     int signal;
132
133     fprintf(stderr, "Now expecting to single-step through "
134             "an instruction %i bytes in size at address 0x%x\n",
135             instr_size, (int) expected_regs.prog_ctr);
136     expected_regs.prog_ctr += instr_size;
137
138     NaClUntrustedThreadsResumeAll(nap);
139     WaitForThreadToFault(nap);
140     NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
141     ASSERT_EQ(GetOnlyThread(nap), natp);
142     ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1);
143     /*
144      * TODO(mseaborn): Move ExceptionToSignal() out of debug_stub and
145      * enable this check on Windows too.
146      */
147     if (!NACL_WINDOWS) {
148       ASSERT_EQ(signal, NACL_ABI_SIGTRAP);
149     }
150
151     NaClAppThreadGetSuspendedRegisters(natp, &regs);
152     if (NACL_WINDOWS) {
153       /*
154        * On Windows, but not elsewhere, the trap flag gets unset after
155        * each instruction, so we must set it again.
156        */
157       regs.flags |= NACL_X86_TRAP_FLAG;
158       NaClAppThreadSetSuspendedRegisters(natp, &regs);
159     }
160     RegsAssertEqual(&regs, &expected_regs);
161   }
162
163   /* Unset the trap flag so that the thread can continue. */
164   regs.flags &= ~NACL_X86_TRAP_FLAG;
165   NaClAppThreadSetSuspendedRegisters(natp, &regs);
166 }
167
168 #elif NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm
169
170 /* ARM does not have hardware single-stepping, so nothing to do here. */
171 void TestSingleStepping(struct NaClAppThread *natp) {
172   UNREFERENCED_PARAMETER(natp);
173 }
174
175 #else
176 # error Unsupported architecture
177 #endif
178
179 void TestReceivingFault(struct NaClApp *nap) {
180   struct NaClSignalContext *expected_regs = StartGuestWithSharedMemory(nap);
181   struct NaClSignalContext regs;
182   struct NaClAppThread *natp;
183   int signal = 0;
184   int dummy_signal;
185
186   WaitForThreadToFault(nap);
187   NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
188   natp = GetOnlyThread(nap);
189   ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1);
190   /*
191    * TODO(mseaborn): Move ExceptionToSignal() out of debug_stub and
192    * enable this check on Windows too.
193    */
194   if (!NACL_WINDOWS) {
195     ASSERT_EQ(signal, kBreakInstructionSignal);
196   }
197
198   /* Check that faulted_thread_count is updated correctly. */
199   ASSERT_EQ(nap->faulted_thread_count, 0);
200   /*
201    * Check that NaClAppThreadUnblockIfFaulted() returns false when
202    * called on a thread that is not blocked.
203    */
204   ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &dummy_signal), 0);
205
206   NaClAppThreadGetSuspendedRegisters(natp, &regs);
207   RegsAssertEqual(&regs, expected_regs);
208
209   /* Skip over the instruction that faulted. */
210   regs.prog_ctr += kBreakInstructionSize;
211   NaClAppThreadSetSuspendedRegisters(natp, &regs);
212
213   TestSingleStepping(natp);
214
215   NaClUntrustedThreadsResumeAll(nap);
216   CHECK(NaClWaitForMainThreadToExit(nap) == 0);
217 }
218
219 /*
220  * This is a test for a bug that occurred only on Mac OS X on x86-32.
221  * If we modify the registers of a suspended thread on x86-32 Mac, we
222  * force the thread to return to untrusted code via the special
223  * trusted routine NaClSwitchRemainingRegsViaECX().  If we later
224  * suspend the thread while it is still executing this routine, we
225  * need to return the untrusted register state that the routine is
226  * attempting to restore -- we test that here.
227  *
228  * Suspending a thread while it is still blocked by a fault is the
229  * easiest way to test this, since this allows us to test suspension
230  * while the thread is at the start of
231  * NaClSwitchRemainingRegsViaECX().  This is also how the debug stub
232  * runs into this problem.
233  */
234 void TestGettingRegistersInMacSwitchRemainingRegs(struct NaClApp *nap) {
235   struct NaClSignalContext *expected_regs = StartGuestWithSharedMemory(nap);
236   struct NaClSignalContext regs;
237   struct NaClAppThread *natp;
238   int signal;
239
240   WaitForThreadToFault(nap);
241   NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
242   natp = GetOnlyThread(nap);
243   NaClAppThreadGetSuspendedRegisters(natp, &regs);
244   RegsAssertEqual(&regs, expected_regs);
245   /*
246    * On Mac OS X on x86-32, this changes the underlying Mac thread's
247    * state so that the thread will execute
248    * NaClSwitchRemainingRegsViaECX().
249    */
250   NaClAppThreadSetSuspendedRegisters(natp, &regs);
251
252   /*
253    * Resume the thread without unblocking it and then re-suspend it.
254    * This causes osx/thread_suspension.c to re-fetch the register
255    * state from the Mac kernel.
256    */
257   NaClUntrustedThreadsResumeAll(nap);
258   NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 1);
259   ASSERT_EQ(GetOnlyThread(nap), natp);
260
261   /* We should get the same register state as before. */
262   NaClAppThreadGetSuspendedRegisters(natp, &regs);
263   RegsAssertEqual(&regs, expected_regs);
264
265   /*
266    * Clean up:  Skip over the instruction that faulted and let the
267    * thread run to completion.
268    */
269   regs.prog_ctr += kBreakInstructionSize;
270   NaClAppThreadSetSuspendedRegisters(natp, &regs);
271   ASSERT_EQ(NaClAppThreadUnblockIfFaulted(natp, &signal), 1);
272   NaClUntrustedThreadsResumeAll(nap);
273   CHECK(NaClWaitForMainThreadToExit(nap) == 0);
274 }
275
276 int main(int argc, char **argv) {
277   struct NaClApp app;
278   struct NaClApp *nap = &app;
279
280   NaClDebugExceptionHandlerStandaloneHandleArgs(argc, argv);
281   NaClHandleBootstrapArgs(&argc, &argv);
282
283   /* Turn off buffering to aid debugging. */
284   setvbuf(stdout, NULL, _IONBF, 0);
285   setvbuf(stderr, NULL, _IONBF, 0);
286
287   NaClAllModulesInit();
288
289   if (argc != 2) {
290     NaClLog(LOG_FATAL, "Expected 1 argument: executable filename\n");
291   }
292
293   CHECK(NaClAppCtor(nap));
294   CHECK(NaClAppLoadFileFromFilename(nap, argv[1]) == LOAD_OK);
295   NaClAppInitialDescriptorHookup(nap);
296
297 #if NACL_LINUX
298   NaClSignalHandlerInit();
299 #elif NACL_OSX
300   CHECK(NaClInterceptMachExceptions());
301 #elif NACL_WINDOWS
302   nap->attach_debug_exception_handler_func =
303       NaClDebugExceptionHandlerStandaloneAttach;
304 #else
305 # error Unknown host OS
306 #endif
307   CHECK(NaClFaultedThreadQueueEnable(nap));
308
309   printf("Running TestReceivingFault...\n");
310   TestReceivingFault(nap);
311
312   printf("Running TestGettingRegistersInMacSwitchRemainingRegs...\n");
313   TestGettingRegistersInMacSwitchRemainingRegs(nap);
314
315   /*
316    * Avoid calling exit() because it runs process-global destructors
317    * which might break code that is running in our unjoined threads.
318    */
319   NaClExit(0);
320 }