1 // Copyright (c) 2012 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.
5 #include "sandbox/win/tests/common/controller.h"
9 #include "base/process/process.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "base/win/windows_version.h"
12 #include "sandbox/win/src/sandbox_factory.h"
16 static const int kDefaultTimeout
= 60000;
18 // Constructs a full path to a file inside the system32 folder.
19 base::string16
MakePathToSys32(const wchar_t* name
, bool is_obj_man_path
) {
20 wchar_t windows_path
[MAX_PATH
] = {0};
21 if (0 == ::GetSystemWindowsDirectoryW(windows_path
, MAX_PATH
))
22 return base::string16();
24 base::string16
full_path(windows_path
);
25 if (full_path
.empty())
29 full_path
.insert(0, L
"\\??\\");
31 full_path
+= L
"\\system32\\";
36 // Constructs a full path to a file inside the syswow64 folder.
37 base::string16
MakePathToSysWow64(const wchar_t* name
, bool is_obj_man_path
) {
38 wchar_t windows_path
[MAX_PATH
] = {0};
39 if (0 == ::GetSystemWindowsDirectoryW(windows_path
, MAX_PATH
))
40 return base::string16();
42 base::string16
full_path(windows_path
);
43 if (full_path
.empty())
47 full_path
.insert(0, L
"\\??\\");
49 full_path
+= L
"\\SysWOW64\\";
54 bool IsProcessRunning(HANDLE process
) {
56 if (::GetExitCodeProcess(process
, &exit_code
))
57 return exit_code
== STILL_ACTIVE
;
65 base::string16
MakePathToSys(const wchar_t* name
, bool is_obj_man_path
) {
66 return (base::win::OSInfo::GetInstance()->wow64_status() ==
67 base::win::OSInfo::WOW64_ENABLED
) ?
68 MakePathToSysWow64(name
, is_obj_man_path
) :
69 MakePathToSys32(name
, is_obj_man_path
);
72 BrokerServices
* GetBroker() {
73 static BrokerServices
* broker
= SandboxFactory::GetBrokerServices();
74 static bool is_initialized
= false;
80 if (!is_initialized
) {
81 if (SBOX_ALL_OK
!= broker
->Init())
84 is_initialized
= true;
90 TestRunner::TestRunner(JobLevel job_level
, TokenLevel startup_token
,
91 TokenLevel main_token
)
92 : is_init_(false), is_async_(false), no_sandbox_(false),
93 target_process_id_(0) {
94 Init(job_level
, startup_token
, main_token
);
97 TestRunner::TestRunner()
98 : is_init_(false), is_async_(false), no_sandbox_(false),
99 target_process_id_(0) {
100 Init(JOB_LOCKDOWN
, USER_RESTRICTED_SAME_ACCESS
, USER_LOCKDOWN
);
103 void TestRunner::Init(JobLevel job_level
, TokenLevel startup_token
,
104 TokenLevel main_token
) {
107 timeout_
= kDefaultTimeout
;
108 state_
= AFTER_REVERT
;
110 kill_on_destruction_
= true;
111 target_process_id_
= 0;
113 broker_
= GetBroker();
117 policy_
= broker_
->CreatePolicy();
121 policy_
->SetJobLevel(job_level
, 0);
122 policy_
->SetTokenLevel(startup_token
, main_token
);
127 TargetPolicy
* TestRunner::GetPolicy() {
131 TestRunner::~TestRunner() {
132 if (target_process_
&& kill_on_destruction_
)
133 ::TerminateProcess(target_process_
, 0);
139 bool TestRunner::AddRule(TargetPolicy::SubSystem subsystem
,
140 TargetPolicy::Semantics semantics
,
141 const wchar_t* pattern
) {
145 return (SBOX_ALL_OK
== policy_
->AddRule(subsystem
, semantics
, pattern
));
148 bool TestRunner::AddRuleSys32(TargetPolicy::Semantics semantics
,
149 const wchar_t* pattern
) {
153 base::string16 win32_path
= MakePathToSys32(pattern
, false);
154 if (win32_path
.empty())
157 if (!AddRule(TargetPolicy::SUBSYS_FILES
, semantics
, win32_path
.c_str()))
160 if (base::win::OSInfo::GetInstance()->wow64_status() !=
161 base::win::OSInfo::WOW64_ENABLED
)
164 win32_path
= MakePathToSysWow64(pattern
, false);
165 if (win32_path
.empty())
168 return AddRule(TargetPolicy::SUBSYS_FILES
, semantics
, win32_path
.c_str());
171 bool TestRunner::AddFsRule(TargetPolicy::Semantics semantics
,
172 const wchar_t* pattern
) {
176 return AddRule(TargetPolicy::SUBSYS_FILES
, semantics
, pattern
);
179 int TestRunner::RunTest(const wchar_t* command
) {
181 return SBOX_TEST_INVALID_PARAMETER
;
183 wchar_t state_number
[2];
184 state_number
[0] = L
'0' + state_
;
185 state_number
[1] = L
'\0';
186 base::string16
full_command(state_number
);
187 full_command
+= L
" ";
188 full_command
+= command
;
190 return InternalRunTest(full_command
.c_str());
193 int TestRunner::InternalRunTest(const wchar_t* command
) {
195 return SBOX_TEST_FAILED_TO_RUN_TEST
;
197 // For simplicity TestRunner supports only one process per instance.
198 if (target_process_
) {
199 if (IsProcessRunning(target_process_
))
200 return SBOX_TEST_FAILED_TO_RUN_TEST
;
201 target_process_
.Close();
202 target_process_id_
= 0;
205 // Get the path to the sandboxed process.
206 wchar_t prog_name
[MAX_PATH
];
207 GetModuleFileNameW(NULL
, prog_name
, MAX_PATH
);
209 // Launch the sandboxed process.
210 ResultCode result
= SBOX_ALL_OK
;
211 PROCESS_INFORMATION target
= {0};
213 base::string16
arguments(L
"\"");
214 arguments
+= prog_name
;
215 arguments
+= L
"\" -child";
216 arguments
+= no_sandbox_
? L
"-no-sandbox " : L
" ";
217 arguments
+= command
;
220 STARTUPINFO startup_info
= {sizeof(STARTUPINFO
)};
221 if (!::CreateProcessW(prog_name
, &arguments
[0], NULL
, NULL
, FALSE
, 0,
222 NULL
, NULL
, &startup_info
, &target
)) {
223 return SBOX_ERROR_GENERIC
;
225 broker_
->AddTargetPeer(target
.hProcess
);
227 result
= broker_
->SpawnTarget(prog_name
, arguments
.c_str(), policy_
,
231 if (SBOX_ALL_OK
!= result
)
232 return SBOX_TEST_FAILED_TO_RUN_TEST
;
234 ::ResumeThread(target
.hThread
);
236 // For an asynchronous run we don't bother waiting.
238 target_process_
.Set(target
.hProcess
);
239 target_process_id_
= target
.dwProcessId
;
240 ::CloseHandle(target
.hThread
);
241 return SBOX_TEST_SUCCEEDED
;
244 if (::IsDebuggerPresent()) {
245 // Don't kill the target process on a time-out while we are debugging.
249 if (WAIT_TIMEOUT
== ::WaitForSingleObject(target
.hProcess
, timeout_
)) {
250 ::TerminateProcess(target
.hProcess
, SBOX_TEST_TIMED_OUT
);
251 ::CloseHandle(target
.hProcess
);
252 ::CloseHandle(target
.hThread
);
253 return SBOX_TEST_TIMED_OUT
;
256 DWORD exit_code
= SBOX_TEST_LAST_RESULT
;
257 if (!::GetExitCodeProcess(target
.hProcess
, &exit_code
)) {
258 ::CloseHandle(target
.hProcess
);
259 ::CloseHandle(target
.hThread
);
260 return SBOX_TEST_FAILED_TO_RUN_TEST
;
263 ::CloseHandle(target
.hProcess
);
264 ::CloseHandle(target
.hThread
);
269 void TestRunner::SetTimeout(DWORD timeout_ms
) {
270 timeout_
= timeout_ms
;
273 void TestRunner::SetTestState(SboxTestsState desired_state
) {
274 state_
= desired_state
;
277 // This is the main procedure for the target (child) application. We'll find out
278 // the target test and call it.
279 // We expect the arguments to be:
280 // argv[1] = "-child"
281 // argv[2] = SboxTestsState when to run the command
282 // argv[3] = command to run
283 // argv[4...] = command arguments.
284 int DispatchCall(int argc
, wchar_t **argv
) {
286 return SBOX_TEST_INVALID_PARAMETER
;
288 // We hard code two tests to avoid dispatch failures.
289 if (0 == _wcsicmp(argv
[3], L
"wait")) {
291 return SBOX_TEST_TIMED_OUT
;
294 if (0 == _wcsicmp(argv
[3], L
"ping"))
295 return SBOX_TEST_PING_OK
;
297 SboxTestsState state
= static_cast<SboxTestsState
>(_wtoi(argv
[2]));
298 if ((state
<= MIN_STATE
) || (state
>= MAX_STATE
))
299 return SBOX_TEST_INVALID_PARAMETER
;
302 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|
303 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
,
304 reinterpret_cast<wchar_t*>(&DispatchCall
), &module
))
305 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
307 std::string command_name
= base::SysWideToMultiByte(argv
[3], CP_UTF8
);
308 CommandFunction command
= reinterpret_cast<CommandFunction
>(
309 ::GetProcAddress(module
, command_name
.c_str()));
311 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
313 if (BEFORE_INIT
== state
)
314 return command(argc
- 4, argv
+ 4);
315 else if (EVERY_STATE
== state
)
316 command(argc
- 4, argv
+ 4);
318 TargetServices
* target
= SandboxFactory::GetTargetServices();
320 if (SBOX_ALL_OK
!= target
->Init())
321 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
323 if (BEFORE_REVERT
== state
)
324 return command(argc
- 4, argv
+ 4);
325 else if (EVERY_STATE
== state
)
326 command(argc
- 4, argv
+ 4);
328 target
->LowerToken();
329 } else if (0 != _wcsicmp(argv
[1], L
"-child-no-sandbox")) {
330 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND
;
333 return command(argc
- 4, argv
+ 4);
336 } // namespace sandbox