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.
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/trusted/service_runtime/include/bits/nacl_syscalls.h"
14 #include "native_client/src/trusted/service_runtime/load_file.h"
15 #include "native_client/src/trusted/service_runtime/nacl_all_modules.h"
16 #include "native_client/src/trusted/service_runtime/nacl_app.h"
17 #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
18 #include "native_client/src/trusted/service_runtime/nacl_copy.h"
19 #include "native_client/src/trusted/service_runtime/nacl_signal.h"
20 #include "native_client/src/trusted/service_runtime/nacl_syscall_common.h"
21 #include "native_client/src/trusted/service_runtime/nacl_syscall_handlers.h"
22 #include "native_client/src/trusted/service_runtime/sel_ldr.h"
23 #include "native_client/tests/signal_handler_single_step/step_test_common.h"
24 #include "native_client/tests/signal_handler_single_step/step_test_syscalls.h"
28 * This test case checks that NaCl's Linux signal handler can be
29 * entered at any point during the context switches between trusted
32 * In particular, this tests that the signal handler correctly
33 * restores %gs on x86-32. This is tricky because NaCl's context
34 * switch code must set %cs and %gs separately, so there is a small
35 * window during which %gs is set to the untrusted-code value but %cs
36 * is not. The signal handler needs to work at this point if we are
37 * to handle asynchronous signals correctly (such as for implementing
41 /* This should be at least 2 so that we test the syscall return path. */
42 static const int kNumberOfCallsToTest = 5;
44 static int g_call_count = 0;
45 static int g_in_untrusted_code = 0;
46 static int g_context_switch_count = 0;
48 static int g_instruction_count = 0;
49 static int g_instruction_byte_count = 0;
50 static int g_jump_count = 0;
51 static nacl_reg_t g_last_prog_ctr = 0;
53 static const char* g_description = NULL;
56 static void SignalSafePrintf(const char *format, ...) {
60 va_start(args, format);
61 len = vsnprintf(buf, sizeof(buf), format, args);
63 SignalSafeWrite(buf, len);
67 * We use a custom NaCl syscall here partly because we need to avoid
68 * making any Linux syscalls while the trap flag is set. On x86-32
69 * Linux, doing a syscall with the trap flag set will sometimes kill
70 * the process with SIGTRAP rather than entering the signal handler.
71 * This might be a kernel bug. x86-64 processes do not have the same
74 static int32_t TestSyscall(struct NaClAppThread *natp) {
75 NaClCopyDropLock(natp->nap);
77 /* Check that the trap flag has not been unset by anything unexpected. */
80 if (++g_call_count == kNumberOfCallsToTest) {
82 NaClReportExitStatus(natp->nap, 0);
83 NaClAppThreadTeardown(natp);
88 static void TrapSignalHandler(int signal,
89 const struct NaClSignalContext *context,
91 if (signal == SIGTRAP) {
92 g_instruction_count++;
94 * This is a heuristic for detecting jumps, based on the maximum
95 * length of an x86 instruction being 15 bytes. We would miss
96 * short forward jumps.
98 if (context->prog_ctr - g_last_prog_ctr > 15) {
101 /* Measure total size of instructions, except for taken branches. */
102 g_instruction_byte_count += context->prog_ctr - g_last_prog_ctr;
104 g_last_prog_ctr = context->prog_ctr;
106 if (g_in_untrusted_code != is_untrusted) {
107 g_context_switch_count++;
108 g_in_untrusted_code = is_untrusted;
110 SignalSafePrintf("Switching to %s: since previous switch: "
111 "%i instructions, %i instruction bytes, %i jumps\n",
112 is_untrusted ? "untrusted" : "trusted",
114 g_instruction_byte_count,
116 if (is_untrusted && g_call_count == kNumberOfCallsToTest - 1) {
117 SignalSafePrintf("RESULT InstructionsPerSyscall: %s= "
118 "%i count\n", g_description, g_instruction_count);
119 SignalSafePrintf("RESULT InstructionBytesPerSyscall: %s= "
121 g_description, g_instruction_byte_count);
122 SignalSafePrintf("RESULT JumpsPerSyscall: %s= "
123 "%i count\n", g_description, g_jump_count);
125 g_instruction_count = 0;
126 g_instruction_byte_count = 0;
130 SignalSafeLogStringLiteral("Error: Received unexpected signal\n");
135 static void ThreadCreateHook(struct NaClAppThread *natp) {
136 UNREFERENCED_PARAMETER(natp);
141 static void ThreadExitHook(struct NaClAppThread *natp) {
142 UNREFERENCED_PARAMETER(natp);
145 static void ProcessExitHook(void) {
148 static const struct NaClDebugCallbacks debug_callbacks = {
154 int main(int argc, char **argv) {
156 struct NaClSyscallTableEntry syscall_table[NACL_MAX_SYSCALLS] = {{0}};
158 for (index = 0; index < NACL_MAX_SYSCALLS; index++) {
159 syscall_table[index].handler = NaClSysNotImplementedDecoder;
161 syscall_table[SINGLE_STEP_TEST_SYSCALL].handler = TestSyscall;
163 NaClAllModulesInit();
166 NaClLog(LOG_FATAL, "Expected 2 arguments: <executable filename> "
167 "<description-string>\n");
169 g_description = argv[2];
171 CHECK(NaClAppWithSyscallTableCtor(&app, syscall_table));
173 app.debug_stub_callbacks = &debug_callbacks;
174 NaClSignalHandlerInit();
175 NaClSignalHandlerSet(TrapSignalHandler);
177 CHECK(NaClAppLoadFileFromFilename(&app, argv[1]) == LOAD_OK);
178 CHECK(NaClCreateMainThread(&app, 0, NULL, NULL));
179 CHECK(NaClWaitForMainThreadToExit(&app) == 0);
181 CHECK(!g_in_untrusted_code);
182 CHECK(g_context_switch_count == kNumberOfCallsToTest * 2);
185 * Avoid calling exit() because it runs process-global destructors
186 * which might break code that is running in our unjoined threads.