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