Roll src/third_party/skia d32087a:1052f51
[chromium-blink-merge.git] / sandbox / win / tests / common / controller.cc
blob4f90fb4f051798b80638b6ab512d550c130c5b2b
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"
7 #include <string>
9 #include "base/memory/shared_memory.h"
10 #include "base/process/process.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/win/windows_version.h"
14 #include "sandbox/win/src/sandbox_factory.h"
16 namespace {
18 static const int kDefaultTimeout = 60000;
20 // Constructs a full path to a file inside the system32 folder.
21 base::string16 MakePathToSys32(const wchar_t* name, bool is_obj_man_path) {
22 wchar_t windows_path[MAX_PATH] = {0};
23 if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH))
24 return base::string16();
26 base::string16 full_path(windows_path);
27 if (full_path.empty())
28 return full_path;
30 if (is_obj_man_path)
31 full_path.insert(0, L"\\??\\");
33 full_path += L"\\system32\\";
34 full_path += name;
35 return full_path;
38 // Constructs a full path to a file inside the syswow64 folder.
39 base::string16 MakePathToSysWow64(const wchar_t* name, bool is_obj_man_path) {
40 wchar_t windows_path[MAX_PATH] = {0};
41 if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH))
42 return base::string16();
44 base::string16 full_path(windows_path);
45 if (full_path.empty())
46 return full_path;
48 if (is_obj_man_path)
49 full_path.insert(0, L"\\??\\");
51 full_path += L"\\SysWOW64\\";
52 full_path += name;
53 return full_path;
56 bool IsProcessRunning(HANDLE process) {
57 DWORD exit_code = 0;
58 if (::GetExitCodeProcess(process, &exit_code))
59 return exit_code == STILL_ACTIVE;
60 return false;
63 } // namespace
65 namespace sandbox {
67 base::string16 MakePathToSys(const wchar_t* name, bool is_obj_man_path) {
68 return (base::win::OSInfo::GetInstance()->wow64_status() ==
69 base::win::OSInfo::WOW64_ENABLED) ?
70 MakePathToSysWow64(name, is_obj_man_path) :
71 MakePathToSys32(name, is_obj_man_path);
74 BrokerServices* GetBroker() {
75 static BrokerServices* broker = SandboxFactory::GetBrokerServices();
76 static bool is_initialized = false;
78 if (!broker) {
79 return NULL;
82 if (!is_initialized) {
83 if (SBOX_ALL_OK != broker->Init())
84 return NULL;
86 is_initialized = true;
89 return broker;
92 TestRunner::TestRunner(JobLevel job_level, TokenLevel startup_token,
93 TokenLevel main_token)
94 : is_init_(false), is_async_(false), no_sandbox_(false),
95 target_process_id_(0) {
96 Init(job_level, startup_token, main_token);
99 TestRunner::TestRunner()
100 : is_init_(false), is_async_(false), no_sandbox_(false),
101 target_process_id_(0) {
102 Init(JOB_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
105 void TestRunner::Init(JobLevel job_level, TokenLevel startup_token,
106 TokenLevel main_token) {
107 broker_ = NULL;
108 policy_ = NULL;
109 timeout_ = kDefaultTimeout;
110 state_ = AFTER_REVERT;
111 is_async_= false;
112 kill_on_destruction_ = true;
113 target_process_id_ = 0;
115 broker_ = GetBroker();
116 if (!broker_)
117 return;
119 policy_ = broker_->CreatePolicy();
120 if (!policy_)
121 return;
123 policy_->SetJobLevel(job_level, 0);
124 policy_->SetTokenLevel(startup_token, main_token);
126 // Close all ALPC ports.
127 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
128 policy_->AddKernelObjectToClose(L"ALPC Port", NULL);
131 is_init_ = true;
134 TargetPolicy* TestRunner::GetPolicy() {
135 return policy_;
138 TestRunner::~TestRunner() {
139 if (target_process_.IsValid() && kill_on_destruction_)
140 ::TerminateProcess(target_process_.Get(), 0);
142 if (policy_)
143 policy_->Release();
146 bool TestRunner::AddRule(TargetPolicy::SubSystem subsystem,
147 TargetPolicy::Semantics semantics,
148 const wchar_t* pattern) {
149 if (!is_init_)
150 return false;
152 return (SBOX_ALL_OK == policy_->AddRule(subsystem, semantics, pattern));
155 bool TestRunner::AddRuleSys32(TargetPolicy::Semantics semantics,
156 const wchar_t* pattern) {
157 if (!is_init_)
158 return false;
160 base::string16 win32_path = MakePathToSys32(pattern, false);
161 if (win32_path.empty())
162 return false;
164 if (!AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str()))
165 return false;
167 if (base::win::OSInfo::GetInstance()->wow64_status() !=
168 base::win::OSInfo::WOW64_ENABLED)
169 return true;
171 win32_path = MakePathToSysWow64(pattern, false);
172 if (win32_path.empty())
173 return false;
175 return AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str());
178 bool TestRunner::AddFsRule(TargetPolicy::Semantics semantics,
179 const wchar_t* pattern) {
180 if (!is_init_)
181 return false;
183 return AddRule(TargetPolicy::SUBSYS_FILES, semantics, pattern);
186 int TestRunner::RunTest(const wchar_t* command) {
187 if (MAX_STATE > 10)
188 return SBOX_TEST_INVALID_PARAMETER;
190 wchar_t state_number[2];
191 state_number[0] = static_cast<wchar_t>(L'0' + state_);
192 state_number[1] = L'\0';
193 base::string16 full_command(state_number);
194 full_command += L" ";
195 full_command += command;
197 return InternalRunTest(full_command.c_str());
200 int TestRunner::InternalRunTest(const wchar_t* command) {
201 if (!is_init_)
202 return SBOX_TEST_FAILED_TO_RUN_TEST;
204 // For simplicity TestRunner supports only one process per instance.
205 if (target_process_.IsValid()) {
206 if (IsProcessRunning(target_process_.Get()))
207 return SBOX_TEST_FAILED_TO_RUN_TEST;
208 target_process_.Close();
209 target_process_id_ = 0;
212 // Get the path to the sandboxed process.
213 wchar_t prog_name[MAX_PATH];
214 GetModuleFileNameW(NULL, prog_name, MAX_PATH);
216 // Launch the sandboxed process.
217 ResultCode result = SBOX_ALL_OK;
218 PROCESS_INFORMATION target = {0};
220 base::string16 arguments(L"\"");
221 arguments += prog_name;
222 arguments += L"\" -child";
223 arguments += no_sandbox_ ? L"-no-sandbox " : L" ";
224 arguments += command;
226 if (no_sandbox_) {
227 STARTUPINFO startup_info = {sizeof(STARTUPINFO)};
228 if (!::CreateProcessW(prog_name, &arguments[0], NULL, NULL, FALSE, 0,
229 NULL, NULL, &startup_info, &target)) {
230 return SBOX_ERROR_GENERIC;
232 broker_->AddTargetPeer(target.hProcess);
233 } else {
234 result = broker_->SpawnTarget(prog_name, arguments.c_str(), policy_,
235 &target);
238 if (SBOX_ALL_OK != result)
239 return SBOX_TEST_FAILED_TO_RUN_TEST;
241 ::ResumeThread(target.hThread);
243 // For an asynchronous run we don't bother waiting.
244 if (is_async_) {
245 target_process_.Set(target.hProcess);
246 target_process_id_ = target.dwProcessId;
247 ::CloseHandle(target.hThread);
248 return SBOX_TEST_SUCCEEDED;
251 if (::IsDebuggerPresent()) {
252 // Don't kill the target process on a time-out while we are debugging.
253 timeout_ = INFINITE;
256 if (WAIT_TIMEOUT == ::WaitForSingleObject(target.hProcess, timeout_)) {
257 ::TerminateProcess(target.hProcess, static_cast<UINT>(SBOX_TEST_TIMED_OUT));
258 ::CloseHandle(target.hProcess);
259 ::CloseHandle(target.hThread);
260 return SBOX_TEST_TIMED_OUT;
263 DWORD exit_code = static_cast<DWORD>(SBOX_TEST_LAST_RESULT);
264 if (!::GetExitCodeProcess(target.hProcess, &exit_code)) {
265 ::CloseHandle(target.hProcess);
266 ::CloseHandle(target.hThread);
267 return SBOX_TEST_FAILED_TO_RUN_TEST;
270 ::CloseHandle(target.hProcess);
271 ::CloseHandle(target.hThread);
273 return exit_code;
276 void TestRunner::SetTimeout(DWORD timeout_ms) {
277 timeout_ = timeout_ms;
280 void TestRunner::SetTestState(SboxTestsState desired_state) {
281 state_ = desired_state;
284 // This is the main procedure for the target (child) application. We'll find out
285 // the target test and call it.
286 // We expect the arguments to be:
287 // argv[1] = "-child"
288 // argv[2] = SboxTestsState when to run the command
289 // argv[3] = command to run
290 // argv[4...] = command arguments.
291 int DispatchCall(int argc, wchar_t **argv) {
292 if (argc < 4)
293 return SBOX_TEST_INVALID_PARAMETER;
295 // We hard code two tests to avoid dispatch failures.
296 if (0 == _wcsicmp(argv[3], L"wait")) {
297 Sleep(INFINITE);
298 return SBOX_TEST_TIMED_OUT;
301 if (0 == _wcsicmp(argv[3], L"ping"))
302 return SBOX_TEST_PING_OK;
304 // If the caller shared a shared memory handle with us attempt to open it
305 // in read only mode and sleep infinitely if we succeed.
306 if (0 == _wcsicmp(argv[3], L"shared_memory_handle")) {
307 base::SharedMemoryHandle shared_handle = NULL;
308 base::StringToUint(
309 argv[4], reinterpret_cast<unsigned int*>(&shared_handle));
310 if (shared_handle == NULL)
311 return SBOX_TEST_INVALID_PARAMETER;
312 base::SharedMemory read_only_view(shared_handle, true);
313 if (!read_only_view.Map(0))
314 return SBOX_TEST_INVALID_PARAMETER;
315 std::string contents(reinterpret_cast<char*>(read_only_view.memory()));
316 if (contents != "Hello World")
317 return SBOX_TEST_INVALID_PARAMETER;
318 Sleep(INFINITE);
319 return SBOX_TEST_TIMED_OUT;
322 SboxTestsState state = static_cast<SboxTestsState>(_wtoi(argv[2]));
323 if ((state <= MIN_STATE) || (state >= MAX_STATE))
324 return SBOX_TEST_INVALID_PARAMETER;
326 HMODULE module;
327 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
328 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
329 reinterpret_cast<wchar_t*>(&DispatchCall), &module))
330 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
332 std::string command_name = base::SysWideToMultiByte(argv[3], CP_UTF8);
333 CommandFunction command = reinterpret_cast<CommandFunction>(
334 ::GetProcAddress(module, command_name.c_str()));
335 if (!command)
336 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
338 if (BEFORE_INIT == state)
339 return command(argc - 4, argv + 4);
340 else if (EVERY_STATE == state)
341 command(argc - 4, argv + 4);
343 TargetServices* target = SandboxFactory::GetTargetServices();
344 if (target) {
345 if (SBOX_ALL_OK != target->Init())
346 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
348 if (BEFORE_REVERT == state)
349 return command(argc - 4, argv + 4);
350 else if (EVERY_STATE == state)
351 command(argc - 4, argv + 4);
353 #if defined(ADDRESS_SANITIZER)
354 // Bind and leak dbghelp.dll before the token is lowered, otherwise
355 // AddressSanitizer will crash when trying to symbolize a report.
356 if (!LoadLibraryA("dbghelp.dll"))
357 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
358 #endif
360 target->LowerToken();
361 } else if (0 != _wcsicmp(argv[1], L"-child-no-sandbox")) {
362 return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
365 return command(argc - 4, argv + 4);
368 } // namespace sandbox