1// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Some tests for the framework itself.
6
7#include "testing/gtest/include/gtest/gtest.h"
8#include "sandbox/win/src/sandbox.h"
9#include "sandbox/win/src/target_services.h"
10#include "sandbox/win/src/sandbox_factory.h"
11#include "sandbox/win/tests/common/controller.h"
12
13namespace sandbox {
14
15// Returns the current process state.
16SBOX_TESTS_COMMAND int IntegrationTestsTest_state(int argc, wchar_t **argv) {
17  if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
18    return BEFORE_INIT;
19
20  if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
21    return BEFORE_REVERT;
22
23  return AFTER_REVERT;
24}
25
26// Returns the current process state, keeping track of it.
27SBOX_TESTS_COMMAND int IntegrationTestsTest_state2(int argc, wchar_t **argv) {
28  static SboxTestsState state = MIN_STATE;
29  if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) {
30    if (MIN_STATE == state)
31      state = BEFORE_INIT;
32    return state;
33  }
34
35  if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) {
36    if (BEFORE_INIT == state)
37      state = BEFORE_REVERT;
38    return state;
39  }
40
41  if (BEFORE_REVERT == state)
42    state =  AFTER_REVERT;
43  return state;
44}
45
46// Blocks the process for argv[0] milliseconds simulating stuck child.
47SBOX_TESTS_COMMAND int IntegrationTestsTest_stuck(int argc, wchar_t **argv) {
48  int timeout = 500;
49  if (argc > 0) {
50    timeout = _wtoi(argv[0]);
51  }
52
53  ::Sleep(timeout);
54  return 1;
55}
56
57// Returns the number of arguments
58SBOX_TESTS_COMMAND int IntegrationTestsTest_args(int argc, wchar_t **argv) {
59  for (int i = 0; i < argc; i++) {
60    wchar_t argument[20];
61    size_t argument_bytes = wcslen(argv[i]) * sizeof(wchar_t);
62    memcpy(argument, argv[i], __min(sizeof(argument), argument_bytes));
63  }
64
65  return argc;
66}
67
68// Creates a job and tries to run a process inside it. The function can be
69// called with up to two parameters. The first one if set to "none" means that
70// the child process should be run with the JOB_NONE JobLevel else it is run
71// with JOB_LOCKDOWN level. The second if present specifies that the
72// JOB_OBJECT_LIMIT_BREAKAWAY_OK flag should be set on the job object created
73// in this function. The return value is either SBOX_TEST_SUCCEEDED if the test
74// has passed or a value between 0 and 4 indicating which part of the test has
75// failed.
76SBOX_TESTS_COMMAND int IntegrationTestsTest_job(int argc, wchar_t **argv) {
77  HANDLE job = ::CreateJobObject(NULL, NULL);
78  if (!job)
79    return 0;
80
81  JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_limits;
82  if (!::QueryInformationJobObject(job, JobObjectExtendedLimitInformation,
83                                   &job_limits, sizeof(job_limits), NULL)) {
84    return 1;
85  }
86  // We cheat here and assume no 2-nd parameter means no breakaway flag and any
87  // value for the second param means with breakaway flag.
88  if (argc > 1) {
89    job_limits.BasicLimitInformation.LimitFlags |=
90        JOB_OBJECT_LIMIT_BREAKAWAY_OK;
91  } else {
92    job_limits.BasicLimitInformation.LimitFlags &=
93        ~JOB_OBJECT_LIMIT_BREAKAWAY_OK;
94  }
95  if (!::SetInformationJobObject(job, JobObjectExtendedLimitInformation,
96                                &job_limits, sizeof(job_limits))) {
97    return 2;
98  }
99  if (!::AssignProcessToJobObject(job, ::GetCurrentProcess()))
100    return 3;
101
102  JobLevel job_level = JOB_LOCKDOWN;
103  if (argc > 0 && wcscmp(argv[0], L"none") == 0)
104    job_level = JOB_NONE;
105
106  TestRunner runner(job_level, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
107  runner.SetTimeout(2000);
108
109  if (1 != runner.RunTest(L"IntegrationTestsTest_args 1"))
110    return 4;
111
112  // Terminate the job now.
113  ::TerminateJobObject(job, SBOX_TEST_SUCCEEDED);
114  // We should not make it to here but it doesn't mean our test failed.
115  return SBOX_TEST_SUCCEEDED;
116}
117
118TEST(IntegrationTestsTest, CallsBeforeInit) {
119  TestRunner runner;
120  runner.SetTimeout(2000);
121  runner.SetTestState(BEFORE_INIT);
122  ASSERT_EQ(BEFORE_INIT, runner.RunTest(L"IntegrationTestsTest_state"));
123}
124
125TEST(IntegrationTestsTest, CallsBeforeRevert) {
126  TestRunner runner;
127  runner.SetTimeout(2000);
128  runner.SetTestState(BEFORE_REVERT);
129  ASSERT_EQ(BEFORE_REVERT, runner.RunTest(L"IntegrationTestsTest_state"));
130}
131
132TEST(IntegrationTestsTest, CallsAfterRevert) {
133  TestRunner runner;
134  runner.SetTimeout(2000);
135  runner.SetTestState(AFTER_REVERT);
136  ASSERT_EQ(AFTER_REVERT, runner.RunTest(L"IntegrationTestsTest_state"));
137}
138
139TEST(IntegrationTestsTest, CallsEveryState) {
140  TestRunner runner;
141  runner.SetTimeout(2000);
142  runner.SetTestState(EVERY_STATE);
143  ASSERT_EQ(AFTER_REVERT, runner.RunTest(L"IntegrationTestsTest_state2"));
144}
145
146TEST(IntegrationTestsTest, ForwardsArguments) {
147  TestRunner runner;
148  runner.SetTimeout(2000);
149  runner.SetTestState(BEFORE_INIT);
150  ASSERT_EQ(1, runner.RunTest(L"IntegrationTestsTest_args first"));
151  ASSERT_EQ(4, runner.RunTest(L"IntegrationTestsTest_args first second third "
152                              L"fourth"));
153}
154
155TEST(IntegrationTestsTest, WaitForStuckChild) {
156  TestRunner runner;
157  runner.SetTimeout(2000);
158  runner.SetAsynchronous(true);
159  runner.SetKillOnDestruction(false);
160  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
161            runner.RunTest(L"IntegrationTestsTest_stuck 100"));
162  ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
163}
164
165TEST(IntegrationTestsTest, NoWaitForStuckChildNoJob) {
166  TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
167  runner.SetTimeout(2000);
168  runner.SetAsynchronous(true);
169  runner.SetKillOnDestruction(false);
170  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
171            runner.RunTest(L"IntegrationTestsTest_stuck 2000"));
172  ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
173  // In this case the processes are not tracked by the broker and should be
174  // still active.
175  DWORD exit_code;
176  ASSERT_TRUE(::GetExitCodeProcess(runner.process(), &exit_code));
177  ASSERT_EQ(STILL_ACTIVE, exit_code);
178  // Terminate the test process now.
179  ::TerminateProcess(runner.process(), 0);
180}
181
182TEST(IntegrationTestsTest, TwoStuckChildrenSecondOneHasNoJob) {
183  TestRunner runner;
184  runner.SetTimeout(2000);
185  runner.SetAsynchronous(true);
186  runner.SetKillOnDestruction(false);
187  TestRunner runner2(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
188  runner2.SetTimeout(2000);
189  runner2.SetAsynchronous(true);
190  runner2.SetKillOnDestruction(false);
191  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
192            runner.RunTest(L"IntegrationTestsTest_stuck 100"));
193  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
194            runner2.RunTest(L"IntegrationTestsTest_stuck 2000"));
195  // Actually both runners share the same singleton broker.
196  ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
197  // In this case the processes are not tracked by the broker and should be
198  // still active.
199  DWORD exit_code;
200  // Checking the exit code for |runner| is flaky on the slow bots but at
201  // least we know that the wait above has succeeded if we are here.
202  ASSERT_TRUE(::GetExitCodeProcess(runner2.process(), &exit_code));
203  ASSERT_EQ(STILL_ACTIVE, exit_code);
204  // Terminate the test process now.
205  ::TerminateProcess(runner2.process(), 0);
206}
207
208TEST(IntegrationTestsTest, TwoStuckChildrenFirstOneHasNoJob) {
209  TestRunner runner;
210  runner.SetTimeout(2000);
211  runner.SetAsynchronous(true);
212  runner.SetKillOnDestruction(false);
213  TestRunner runner2(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
214  runner2.SetTimeout(2000);
215  runner2.SetAsynchronous(true);
216  runner2.SetKillOnDestruction(false);
217  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
218            runner2.RunTest(L"IntegrationTestsTest_stuck 2000"));
219  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
220            runner.RunTest(L"IntegrationTestsTest_stuck 100"));
221  // Actually both runners share the same singleton broker.
222  ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
223  // In this case the processes are not tracked by the broker and should be
224  // still active.
225  DWORD exit_code;
226  // Checking the exit code for |runner| is flaky on the slow bots but at
227  // least we know that the wait above has succeeded if we are here.
228  ASSERT_TRUE(::GetExitCodeProcess(runner2.process(), &exit_code));
229  ASSERT_EQ(STILL_ACTIVE, exit_code);
230  // Terminate the test process now.
231  ::TerminateProcess(runner2.process(), 0);
232}
233
234TEST(IntegrationTestsTest, MultipleStuckChildrenSequential) {
235  TestRunner runner;
236  runner.SetTimeout(2000);
237  runner.SetAsynchronous(true);
238  runner.SetKillOnDestruction(false);
239  TestRunner runner2(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
240  runner2.SetTimeout(2000);
241  runner2.SetAsynchronous(true);
242  runner2.SetKillOnDestruction(false);
243
244  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
245            runner.RunTest(L"IntegrationTestsTest_stuck 100"));
246  // Actually both runners share the same singleton broker.
247  ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
248  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
249            runner2.RunTest(L"IntegrationTestsTest_stuck 2000"));
250  // Actually both runners share the same singleton broker.
251  ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
252
253  DWORD exit_code;
254  // Checking the exit code for |runner| is flaky on the slow bots but at
255  // least we know that the wait above has succeeded if we are here.
256  ASSERT_TRUE(::GetExitCodeProcess(runner2.process(), &exit_code));
257  ASSERT_EQ(STILL_ACTIVE, exit_code);
258  // Terminate the test process now.
259  ::TerminateProcess(runner2.process(), 0);
260
261  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
262            runner.RunTest(L"IntegrationTestsTest_stuck 100"));
263  // Actually both runners share the same singleton broker.
264  ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
265}
266
267// Running from inside job that allows us to escape from it should be ok.
268TEST(IntegrationTestsTest, RunChildFromInsideJob) {
269  TestRunner runner;
270  runner.SetUnsandboxed(true);
271  runner.SetTimeout(2000);
272  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
273            runner.RunTest(L"IntegrationTestsTest_job with_job escape_flag"));
274}
275
276// Running from inside job that doesn't allow us to escape from it should fail
277// on any windows prior to 8.
278TEST(IntegrationTestsTest, RunChildFromInsideJobNoEscape) {
279  int expect_result = 4;  // Means the runner has failed to execute the child.
280  // Check if we are on Win8 or newer and expect a success as newer windows
281  // versions support nested jobs.
282  OSVERSIONINFOEX version_info = { sizeof version_info };
283  ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
284  if (version_info.dwMajorVersion > 6 ||
285      (version_info.dwMajorVersion == 6 && version_info.dwMinorVersion >= 2)) {
286    expect_result = SBOX_TEST_SUCCEEDED;
287  }
288
289  TestRunner runner;
290  runner.SetUnsandboxed(true);
291  runner.SetTimeout(2000);
292  ASSERT_EQ(expect_result,
293            runner.RunTest(L"IntegrationTestsTest_job with_job"));
294}
295
296// Running without a job object should be ok regardless of the fact that we are
297// running inside an outter job.
298TEST(IntegrationTestsTest, RunJoblessChildFromInsideJob) {
299  TestRunner runner;
300  runner.SetUnsandboxed(true);
301  runner.SetTimeout(2000);
302  ASSERT_EQ(SBOX_TEST_SUCCEEDED,
303            runner.RunTest(L"IntegrationTestsTest_job none"));
304}
305
306}  // namespace sandbox
307