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/shared/platform/nacl_log.h"
8 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
9 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
10 : #include "native_client/src/trusted/service_runtime/thread_suspension.h"
11 : #include "native_client/src/trusted/desc/nrd_all_modules.h"
12 :
13 : #include "gtest/gtest.h"
14 :
15 :
16 : // These test cases test suspension of untrusted threads. They use
17 : // mock untrusted threads, so they do not test interactions between
18 : // thread suspension and context switches between trusted and
19 : // untrusted code. See tests/thread_suspension for similar tests that
20 : // operate on real untrusted threads.
21 :
22 :
23 : // These tests work with the Windows and Mac implementations of thread
24 : // suspension, but the Linux implementation (based on asynchronous
25 : // signals) requires more of NaClAppThread to be present than we mock
26 : // out here. See tests/thread_suspension for a more integrationy test
27 : // that works on Linux.
28 : #if !NACL_LINUX
29 :
30 4 : class ThreadSuspensionTest : public testing::Test {
31 : protected:
32 : virtual void SetUp();
33 : virtual void TearDown();
34 : };
35 :
36 : void ThreadSuspensionTest::SetUp() {
37 2 : NaClNrdAllModulesInit();
38 2 : }
39 :
40 : void ThreadSuspensionTest::TearDown() {
41 2 : NaClNrdAllModulesFini();
42 2 : }
43 :
44 :
45 : struct ThreadArgs {
46 : struct NaClAppThread *natp;
47 : volatile uint32_t var;
48 : volatile bool should_exit;
49 : };
50 :
51 : // Perform some minimal initialisation of the NaClAppThread based on
52 : // what we observe we need for the test. Reusing NaClAppThreadMake()
53 : // here is difficult because it launches an untrusted thread.
54 2 : static void AppThreadInitMinimal(struct NaClAppThread *natp) {
55 2 : memset(natp, 0xff, sizeof(*natp));
56 2 : natp->suspend_state = NACL_APP_THREAD_UNTRUSTED;
57 2 : natp->suspended_registers = NULL;
58 10 : ASSERT_EQ(NaClMutexCtor(&natp->suspend_mu), 1);
59 2 : }
60 :
61 :
62 : // This test checks that after NaClUntrustedThreadsSuspendAll() has
63 : // returned, untrusted threads are completely suspended. We test this
64 : // by running a thread that writes to a memory location. We check
65 : // that the memory location does not change after
66 : // NaClUntrustedThreadsSuspendAll() but does change after
67 : // NaClUntrustedThreadsResumeAll().
68 : //
69 : // This is a regression test for
70 : // http://code.google.com/p/nativeclient/issues/detail?id=2557
71 : // The gotcha in the Windows API is that SuspendThread() can return
72 : // before the thread has really been suspended.
73 : //
74 : // This is technically a stress test, but it was able to reproduce the
75 : // problem very reliably, at least on a multicore machine.
76 :
77 1 : static void WINAPI MutatorThread(void *arg) {
78 1 : struct ThreadArgs *args = (struct ThreadArgs *) arg;
79 1 : uint32_t next_val = 0;
80 106630 : while (!args->should_exit) {
81 106628 : args->var = next_val++;
82 106628 : }
83 1 : }
84 :
85 1 : static void TrySuspendingMutatorThread(struct NaClApp *nap,
86 1 : volatile uint32_t *addr) {
87 : // Wait for guest program to start writing to the address.
88 14608 : while (*addr == 0) { /* do nothing */ }
89 :
90 202 : for (int iteration = 0; iteration < 100; iteration++) {
91 100 : NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 0);
92 100 : uint32_t snapshot = *addr;
93 20000200 : for (int count = 0; count < 100000; count++) {
94 10000000 : uint32_t snapshot2 = *addr;
95 10000000 : if (snapshot2 != snapshot) {
96 0 : NaClLog(LOG_FATAL,
97 : "Read %i but expected %i on try %i of iteration %i\n",
98 : (int) snapshot2, (int) snapshot, count, iteration);
99 0 : }
100 10000000 : }
101 100 : NaClUntrustedThreadsResumeAll(nap);
102 : // Wait for guest program to resume writing.
103 1077222 : while (*addr == snapshot) { /* do nothing */ }
104 100 : }
105 1 : }
106 :
107 8 : TEST_F(ThreadSuspensionTest, TestThreadSuspendsSynchronously) {
108 1 : struct NaClApp app;
109 5 : ASSERT_EQ(NaClAppCtor(&app), 1);
110 :
111 1 : struct NaClAppThread app_thread;
112 1 : AppThreadInitMinimal(&app_thread);
113 :
114 1 : struct ThreadArgs thread_args;
115 1 : thread_args.natp = NULL;
116 1 : thread_args.var = 0;
117 1 : thread_args.should_exit = false;
118 1 : app_thread.host_thread_is_defined = 1;
119 5 : ASSERT_EQ(NaClThreadCreateJoinable(&app_thread.host_thread, MutatorThread,
120 : &thread_args, NACL_KERN_STACK_SIZE), 1);
121 6 : ASSERT_EQ(NaClAddThread(&app, &app_thread), 0);
122 1 : TrySuspendingMutatorThread(&app, &thread_args.var);
123 1 : thread_args.should_exit = true;
124 1 : NaClThreadJoin(&app_thread.host_thread);
125 2 : }
126 :
127 :
128 : // The test below checks that we do not get a deadlock when using
129 : // NaClUntrustedThreadsSuspendAll() on threads that cross between
130 : // untrusted and trusted code by invoking NaCl syscalls.
131 : //
132 : // This is a stress test. It is not guaranteed to find a problem, but
133 : // it did reproduce the problem in the original buggy version of the
134 : // code when Sleep() calls were inserted in suitable places.
135 : //
136 : // See http://code.google.com/p/nativeclient/issues/detail?id=2569
137 :
138 1 : static void WINAPI SyscallInvokerThread(void *arg) {
139 1 : struct ThreadArgs *args = (struct ThreadArgs *) arg;
140 1 : uint32_t next_val = 0;
141 201 : while (!args->should_exit) {
142 199 : NaClAppThreadSetSuspendState(args->natp, NACL_APP_THREAD_UNTRUSTED,
143 : NACL_APP_THREAD_TRUSTED);
144 199 : args->var = next_val++;
145 199 : NaClAppThreadSetSuspendState(args->natp, NACL_APP_THREAD_TRUSTED,
146 : NACL_APP_THREAD_UNTRUSTED);
147 199 : }
148 1 : }
149 :
150 1 : static void TrySuspendingSyscallInvokerThread(struct NaClApp *nap,
151 1 : volatile uint32_t *addr) {
152 : // Wait for guest program to start writing to the address.
153 36928 : while (*addr == 0) { /* do nothing */ }
154 :
155 202 : for (int iteration = 0; iteration < 100; iteration++) {
156 100 : NaClUntrustedThreadsSuspendAll(nap, /* save_registers= */ 0);
157 100 : NaClUntrustedThreadsResumeAll(nap);
158 :
159 : // Wait for guest program to make some progress.
160 100 : uint32_t snapshot = *addr;
161 708020 : while (*addr == snapshot) { /* do nothing */ }
162 100 : }
163 1 : }
164 :
165 8 : TEST_F(ThreadSuspensionTest, TestNoDeadlockInSyscallCrossing) {
166 1 : struct NaClApp app;
167 5 : ASSERT_EQ(NaClAppCtor(&app), 1);
168 :
169 1 : struct NaClAppThread app_thread;
170 1 : AppThreadInitMinimal(&app_thread);
171 :
172 1 : struct ThreadArgs thread_args;
173 1 : thread_args.natp = &app_thread;
174 1 : thread_args.var = 0;
175 1 : thread_args.should_exit = false;
176 1 : app_thread.host_thread_is_defined = 1;
177 5 : ASSERT_EQ(NaClThreadCreateJoinable(&app_thread.host_thread,
178 : SyscallInvokerThread,
179 : &thread_args, NACL_KERN_STACK_SIZE), 1);
180 6 : ASSERT_EQ(NaClAddThread(&app, &app_thread), 0);
181 1 : TrySuspendingSyscallInvokerThread(&app, &thread_args.var);
182 1 : thread_args.should_exit = true;
183 1 : NaClThreadJoin(&app_thread.host_thread);
184 2 : }
185 :
186 : #endif
|