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_host_desc.h"
8 : #include "native_client/src/shared/platform/nacl_log.h"
9 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
10 : #include "native_client/src/trusted/service_runtime/nacl_text.h"
11 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
12 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
13 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
14 : #include "native_client/src/trusted/desc/nrd_all_modules.h"
15 :
16 : #include "gtest/gtest.h"
17 :
18 : //
19 : // There are several problems in how these tests are set up.
20 : //
21 : // 1. NaCl modules such as the Log module are supposed to be
22 : // initialized at process startup and finalized at shutdown. In
23 : // particular, there should not be any threads other than the main
24 : // thread running when the Log module initializes, since the verbosity
25 : // level is set then -- and thereafter it is assumed to be invariant
26 : // and read without acquring locks. If any threads are left running
27 : // (e.g., NaClApp internal service threads), then race detectors would
28 : // legitimately report an error which is inappropriate because the
29 : // test is ignoring the API contract.
30 : //
31 : // 2. NaClApp objects, while they don't have a Dtor, are expected to
32 : // have a lifetime equal to that of the process that contain them. In
33 : // particular, when the untrusted thread invokes the exit syscall, it
34 : // expects to be able to use _exit to exit, killing all other
35 : // untrusted threads as a side effect. Furthermore, once a NaClApp
36 : // object is initialized and NaClAppLaunchServiceThreads invoked,
37 : // system service threads are running holding references to the
38 : // NaClApp object. If the NaClApp object goes out of scope or is
39 : // otherwise destroyed and its memory freed, then these system thread
40 : // may access memory that is no longer valid. Tests cannot readily be
41 : // written to cleanly exercise the state space of a NaClApp after
42 : // NaClAppLaunchServiceThreads unless the test process exits---thereby
43 : // killing the service threads as a side-effect---when each individual
44 : // test is complete.
45 : //
46 : // These tests do not invoke NaClAppLaunchServiceThreads, so there
47 : // should be no service threads left running between tests.
48 :
49 10 : class SelLdrTest : public testing::Test {
50 : protected:
51 : virtual void SetUp();
52 : virtual void TearDown();
53 : };
54 :
55 5 : void SelLdrTest::SetUp() {
56 5 : NaClNrdAllModulesInit();
57 5 : }
58 :
59 5 : void SelLdrTest::TearDown() {
60 5 : NaClNrdAllModulesFini();
61 5 : }
62 :
63 : // set, get, setavail operations on the descriptor table
64 4 : TEST_F(SelLdrTest, DescTable) {
65 : struct NaClApp app;
66 : struct NaClHostDesc *host_desc;
67 : struct NaClDesc* io_desc;
68 : struct NaClDesc* ret_desc;
69 : int ret_code;
70 :
71 1 : ret_code = NaClAppCtor(&app);
72 1 : ASSERT_EQ(1, ret_code);
73 :
74 1 : host_desc = (struct NaClHostDesc *) malloc(sizeof *host_desc);
75 1 : if (NULL == host_desc) {
76 0 : fprintf(stderr, "No memory\n");
77 : }
78 1 : ASSERT_TRUE(NULL != host_desc);
79 :
80 1 : io_desc = (struct NaClDesc *) NaClDescIoDescMake(host_desc);
81 :
82 : // 1st pos available is 0
83 1 : ret_code = NaClSetAvail(&app, io_desc);
84 1 : ASSERT_EQ(0, ret_code);
85 : // valid desc at pos 0
86 1 : ret_desc = NaClGetDesc(&app, 0);
87 1 : ASSERT_TRUE(NULL != ret_desc);
88 :
89 : // next pos available is 1
90 1 : ret_code = NaClSetAvail(&app, NULL);
91 1 : ASSERT_EQ(1, ret_code);
92 : // no desc at pos 1
93 1 : ret_desc = NaClGetDesc(&app, 1);
94 1 : ASSERT_TRUE(NULL == ret_desc);
95 :
96 : // no desc at pos 1 -> pos 1 is available
97 1 : ret_code = NaClSetAvail(&app, io_desc);
98 1 : ASSERT_EQ(1, ret_code);
99 :
100 : // valid desc at pos 1
101 1 : ret_desc = NaClGetDesc(&app, 1);
102 1 : ASSERT_TRUE(NULL != ret_desc);
103 :
104 : // set no desc at pos 3
105 1 : NaClSetDesc(&app, 3, NULL);
106 :
107 : // valid desc at pos 4
108 1 : NaClSetDesc(&app, 4, io_desc);
109 1 : ret_desc = NaClGetDesc(&app, 4);
110 1 : ASSERT_TRUE(NULL != ret_desc);
111 :
112 : // never set a desc at pos 10
113 1 : ret_desc = NaClGetDesc(&app, 10);
114 1 : ASSERT_TRUE(NULL == ret_desc);
115 : }
116 :
117 : // create service socket
118 4 : TEST_F(SelLdrTest, CreateServiceSocket) {
119 : struct NaClApp app;
120 : int ret_code;
121 :
122 1 : ret_code = NaClAppCtor(&app);
123 1 : ASSERT_EQ(1, ret_code);
124 :
125 : // CreateServiceSocket sets the app service_port to a service port
126 : // desc and service_address to a service
127 1 : ASSERT_TRUE(NULL == app.service_port);
128 1 : ASSERT_TRUE(NULL == app.service_address);
129 1 : NaClCreateServiceSocket(&app);
130 1 : ASSERT_TRUE(NULL != app.service_port);
131 1 : ASSERT_TRUE(NULL != app.service_address);
132 : }
133 :
134 : // add and remove operations on the threads table
135 : // Remove thread from an empty table is tested in a death test.
136 : // TODO(tuduce): specify the death test name when checking in.
137 4 : TEST_F(SelLdrTest, ThreadTableTest) {
138 : struct NaClApp app;
139 1 : struct NaClAppThread nat, *appt=&nat;
140 : int ret_code;
141 :
142 1 : ret_code = NaClAppCtor(&app);
143 1 : ASSERT_EQ(1, ret_code);
144 :
145 : // 1st pos available is 0
146 1 : ASSERT_EQ(0, app.num_threads);
147 1 : ret_code = NaClAddThread(&app, appt);
148 1 : ASSERT_EQ(0, ret_code);
149 1 : ASSERT_EQ(1, app.num_threads);
150 :
151 : // next pos available is 1
152 1 : ret_code = NaClAddThread(&app, NULL);
153 1 : ASSERT_EQ(1, ret_code);
154 1 : ASSERT_EQ(2, app.num_threads);
155 :
156 : // no thread at pos 1 -> pos 1 is available
157 1 : ret_code = NaClAddThread(&app, appt);
158 1 : ASSERT_EQ(1, ret_code);
159 1 : ASSERT_EQ(3, app.num_threads);
160 :
161 1 : NaClRemoveThread(&app, 0);
162 1 : ASSERT_EQ(2, app.num_threads);
163 : }
164 :
165 4 : TEST_F(SelLdrTest, MinimumThreadGenerationTest) {
166 : struct NaClApp app;
167 1 : ASSERT_EQ(1, NaClAppCtor(&app));
168 1 : ASSERT_EQ(INT_MAX, NaClMinimumThreadGeneration(&app));
169 :
170 : struct NaClAppThread thread1;
171 : struct NaClAppThread thread2;
172 : // Perform some minimal initialisation of our NaClAppThreads based
173 : // on what we know NaClMinimumThreadGeneration() does. Reusing
174 : // NaClAppThreadCtor() here is difficult because it launches an
175 : // untrusted thread.
176 1 : memset(&thread1, 0xff, sizeof(thread1));
177 1 : memset(&thread2, 0xff, sizeof(thread2));
178 1 : ASSERT_EQ(1, NaClMutexCtor(&thread1.mu));
179 1 : ASSERT_EQ(1, NaClMutexCtor(&thread2.mu));
180 1 : thread1.dynamic_delete_generation = 200;
181 1 : thread2.dynamic_delete_generation = 100;
182 :
183 1 : ASSERT_EQ(0, NaClAddThread(&app, &thread1));
184 1 : ASSERT_EQ(200, NaClMinimumThreadGeneration(&app));
185 1 : ASSERT_EQ(1, NaClAddThread(&app, &thread2));
186 1 : ASSERT_EQ(100, NaClMinimumThreadGeneration(&app));
187 :
188 1 : thread2.dynamic_delete_generation = 300;
189 1 : ASSERT_EQ(200, NaClMinimumThreadGeneration(&app));
190 :
191 : // This is a regression test for
192 : // http://code.google.com/p/nativeclient/issues/detail?id=2190.
193 : // The thread array can contain NULL entries where threads have
194 : // exited and been removed. NaClMinimumThreadGeneration() should
195 : // know to skip those. Also, if it wrongly uses num_threads instead
196 : // of threads.num_entries it will miss thread2 and not return 300.
197 1 : NaClRemoveThread(&app, 0);
198 1 : ASSERT_EQ(300, NaClMinimumThreadGeneration(&app));
199 : }
200 :
201 4 : TEST_F(SelLdrTest, NaClUserToSysAddrRangeTest) {
202 : struct NaClApp app;
203 :
204 1 : ASSERT_EQ(1, NaClAppCtor(&app));
205 : /*
206 : * addr_bits set appropriately. mem_start is 0, which is bogus but
207 : * doesn't matter wrt to what this is testing.
208 : */
209 : uintptr_t addr_test;
210 : size_t obj_size;
211 :
212 1 : obj_size = 16;
213 :
214 : /*
215 : * small object placement
216 : */
217 1 : addr_test = 65536;
218 1 : ASSERT_EQ(addr_test,
219 : NaClUserToSysAddrRange(&app, addr_test, obj_size));
220 :
221 1 : addr_test = ((uintptr_t) 1U << app.addr_bits) - obj_size;
222 1 : ASSERT_EQ(addr_test,
223 : NaClUserToSysAddrRange(&app, addr_test, obj_size));
224 :
225 1 : addr_test = ((uintptr_t) 1U << app.addr_bits) - obj_size + 1;
226 1 : ASSERT_EQ(kNaClBadAddress,
227 : NaClUserToSysAddrRange(&app, addr_test, obj_size));
228 :
229 : /* size-based exceed range */
230 1 : addr_test = 65536;
231 1 : obj_size = ((uintptr_t) 1U << app.addr_bits) - addr_test;
232 1 : ASSERT_EQ(addr_test,
233 : NaClUserToSysAddrRange(&app, addr_test, obj_size));
234 :
235 1 : addr_test = 65536;
236 1 : obj_size = ((uintptr_t) 1U << app.addr_bits) - addr_test + 1;
237 1 : ASSERT_EQ(kNaClBadAddress,
238 : NaClUserToSysAddrRange(&app, addr_test, obj_size));
239 :
240 : /*
241 : * wraparound; assumes ~(uintptr_t) 0 is greater than
242 : * ((uintptr_t) 1U) << app.addr_bits
243 : */
244 :
245 1 : addr_test = 65536;
246 1 : obj_size = ~(uintptr_t) 0U - addr_test;
247 1 : ASSERT_EQ(kNaClBadAddress,
248 : NaClUserToSysAddrRange(&app, addr_test, obj_size));
249 :
250 1 : addr_test = 65536;
251 1 : obj_size = ~(uintptr_t) 0U - addr_test + 1;
252 1 : ASSERT_EQ(kNaClBadAddress,
253 : NaClUserToSysAddrRange(&app, addr_test, obj_size));
254 2 : }
|