Add ICU message format support
[chromium-blink-merge.git] / sandbox / win / src / target_process.cc
blob9b85d991ffb751667ed6cf31f83f279a50834c85
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/src/target_process.h"
7 #include "base/basictypes.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/win/pe_image.h"
10 #include "base/win/startup_information.h"
11 #include "base/win/windows_version.h"
12 #include "sandbox/win/src/crosscall_server.h"
13 #include "sandbox/win/src/crosscall_client.h"
14 #include "sandbox/win/src/policy_low_level.h"
15 #include "sandbox/win/src/sandbox_types.h"
16 #include "sandbox/win/src/sharedmem_ipc_server.h"
17 #include "sandbox/win/src/win_utils.h"
19 namespace {
21 void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
22 if (!source || !size)
23 return;
24 memcpy(dest, source, size);
25 sandbox::PolicyGlobal* policy =
26 reinterpret_cast<sandbox::PolicyGlobal*>(dest);
28 size_t offset = reinterpret_cast<size_t>(source);
30 for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) {
31 size_t buffer = reinterpret_cast<size_t>(policy->entry[i]);
32 if (buffer) {
33 buffer -= offset;
34 policy->entry[i] = reinterpret_cast<sandbox::PolicyBuffer*>(buffer);
41 namespace sandbox {
43 SANDBOX_INTERCEPT HANDLE g_shared_section;
44 SANDBOX_INTERCEPT size_t g_shared_IPC_size;
45 SANDBOX_INTERCEPT size_t g_shared_policy_size;
47 // Returns the address of the main exe module in memory taking in account
48 // address space layout randomization.
49 void* GetBaseAddress(const wchar_t* exe_name, void* entry_point) {
50 HMODULE exe = ::LoadLibrary(exe_name);
51 if (NULL == exe)
52 return exe;
54 base::win::PEImage pe(exe);
55 if (!pe.VerifyMagic()) {
56 ::FreeLibrary(exe);
57 return exe;
59 PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders();
60 char* base = reinterpret_cast<char*>(entry_point) -
61 nt_header->OptionalHeader.AddressOfEntryPoint;
63 ::FreeLibrary(exe);
64 return base;
68 TargetProcess::TargetProcess(base::win::ScopedHandle initial_token,
69 base::win::ScopedHandle lockdown_token,
70 HANDLE job, ThreadProvider* thread_pool)
71 // This object owns everything initialized here except thread_pool and
72 // the job_ handle. The Job handle is closed by BrokerServices and results
73 // eventually in a call to our dtor.
74 : lockdown_token_(lockdown_token.Pass()),
75 initial_token_(initial_token.Pass()),
76 job_(job),
77 thread_pool_(thread_pool),
78 base_address_(NULL) {
81 TargetProcess::~TargetProcess() {
82 DWORD exit_code = 0;
83 // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
84 // will take effect only when the context changes. As far as the testing went,
85 // this wait was enough to switch context and kill the processes in the job.
86 // If this process is already dead, the function will return without waiting.
87 // TODO(nsylvain): If the process is still alive at the end, we should kill
88 // it. http://b/893891
89 // For now, this wait is there only to do a best effort to prevent some leaks
90 // from showing up in purify.
91 if (sandbox_process_info_.IsValid()) {
92 ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50);
93 // At this point, the target process should have been killed. Check.
94 if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(),
95 &exit_code) || (STILL_ACTIVE == exit_code)) {
96 // Something went wrong. We don't know if the target is in a state where
97 // it can manage to do another IPC call. If it can, and we've destroyed
98 // the |ipc_server_|, it will crash the broker. So we intentionally leak
99 // that.
100 if (shared_section_.IsValid())
101 shared_section_.Take();
102 ipc_server_.release();
103 sandbox_process_info_.TakeProcessHandle();
104 return;
108 // ipc_server_ references our process handle, so make sure the former is shut
109 // down before the latter is closed (by ScopedProcessInformation).
110 ipc_server_.reset();
113 // Creates the target (child) process suspended and assigns it to the job
114 // object.
115 DWORD TargetProcess::Create(const wchar_t* exe_path,
116 const wchar_t* command_line,
117 bool inherit_handles,
118 bool set_lockdown_token_after_create,
119 const base::win::StartupInformation& startup_info,
120 base::win::ScopedProcessInformation* target_info) {
121 if (set_lockdown_token_after_create &&
122 base::win::GetVersion() < base::win::VERSION_WIN8) {
123 // We don't allow set_lockdown_token_after_create below Windows 8.
124 return ERROR_INVALID_PARAMETER;
127 exe_name_.reset(_wcsdup(exe_path));
129 // the command line needs to be writable by CreateProcess().
130 scoped_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line));
132 // Start the target process suspended.
133 DWORD flags =
134 CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
136 if (startup_info.has_extended_startup_info())
137 flags |= EXTENDED_STARTUPINFO_PRESENT;
139 if (job_ && base::win::GetVersion() < base::win::VERSION_WIN8) {
140 // Windows 8 implements nested jobs, but for older systems we need to
141 // break out of any job we're in to enforce our restrictions.
142 flags |= CREATE_BREAKAWAY_FROM_JOB;
145 base::win::ScopedHandle scoped_lockdown_token(lockdown_token_.Take());
146 PROCESS_INFORMATION temp_process_info = {};
147 if (set_lockdown_token_after_create) {
148 // First create process with a default token and then replace it later,
149 // after setting primary thread token. This is required for setting
150 // an AppContainer token along with an impersonation token.
151 if (!::CreateProcess(exe_path,
152 cmd_line.get(),
153 NULL, // No security attribute.
154 NULL, // No thread attribute.
155 inherit_handles,
156 flags,
157 NULL, // Use the environment of the caller.
158 NULL, // Use current directory of the caller.
159 startup_info.startup_info(),
160 &temp_process_info)) {
161 return ::GetLastError();
163 } else {
164 if (!::CreateProcessAsUserW(scoped_lockdown_token.Get(),
165 exe_path,
166 cmd_line.get(),
167 NULL, // No security attribute.
168 NULL, // No thread attribute.
169 inherit_handles,
170 flags,
171 NULL, // Use the environment of the caller.
172 NULL, // Use current directory of the caller.
173 startup_info.startup_info(),
174 &temp_process_info)) {
175 return ::GetLastError();
178 base::win::ScopedProcessInformation process_info(temp_process_info);
180 DWORD win_result = ERROR_SUCCESS;
182 if (job_) {
183 // Assign the suspended target to the windows job object.
184 if (!::AssignProcessToJobObject(job_, process_info.process_handle())) {
185 win_result = ::GetLastError();
186 ::TerminateProcess(process_info.process_handle(), 0);
187 return win_result;
191 if (initial_token_.IsValid()) {
192 // Change the token of the main thread of the new process for the
193 // impersonation token with more rights. This allows the target to start;
194 // otherwise it will crash too early for us to help.
195 HANDLE temp_thread = process_info.thread_handle();
196 if (!::SetThreadToken(&temp_thread, initial_token_.Get())) {
197 win_result = ::GetLastError();
198 // It might be a security breach if we let the target run outside the job
199 // so kill it before it causes damage.
200 ::TerminateProcess(process_info.process_handle(), 0);
201 return win_result;
203 initial_token_.Close();
206 if (set_lockdown_token_after_create) {
207 PROCESS_ACCESS_TOKEN process_access_token;
208 process_access_token.thread = process_info.thread_handle();
209 process_access_token.token = scoped_lockdown_token.Get();
211 NtSetInformationProcess SetInformationProcess = NULL;
212 ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess);
214 NTSTATUS status = SetInformationProcess(
215 process_info.process_handle(),
216 static_cast<PROCESS_INFORMATION_CLASS>(NtProcessInformationAccessToken),
217 &process_access_token,
218 sizeof(process_access_token));
219 if (!NT_SUCCESS(status)) {
220 win_result = ::GetLastError();
221 ::TerminateProcess(process_info.process_handle(), 0); // exit code
222 return win_result;
226 CONTEXT context;
227 context.ContextFlags = CONTEXT_ALL;
228 if (!::GetThreadContext(process_info.thread_handle(), &context)) {
229 win_result = ::GetLastError();
230 ::TerminateProcess(process_info.process_handle(), 0);
231 return win_result;
234 #if defined(_WIN64)
235 void* entry_point = reinterpret_cast<void*>(context.Rcx);
236 #else
237 #pragma warning(push)
238 #pragma warning(disable: 4312)
239 // This cast generates a warning because it is 32 bit specific.
240 void* entry_point = reinterpret_cast<void*>(context.Eax);
241 #pragma warning(pop)
242 #endif // _WIN64
244 if (!target_info->DuplicateFrom(process_info)) {
245 win_result = ::GetLastError(); // This may or may not be correct.
246 ::TerminateProcess(process_info.process_handle(), 0);
247 return win_result;
250 base_address_ = GetBaseAddress(exe_path, entry_point);
251 sandbox_process_info_.Set(process_info.Take());
252 return win_result;
255 ResultCode TargetProcess::TransferVariable(const char* name, void* address,
256 size_t size) {
257 if (!sandbox_process_info_.IsValid())
258 return SBOX_ERROR_UNEXPECTED_CALL;
260 void* child_var = address;
262 #if SANDBOX_EXPORTS
263 HMODULE module = ::LoadLibrary(exe_name_.get());
264 if (NULL == module)
265 return SBOX_ERROR_GENERIC;
267 child_var = ::GetProcAddress(module, name);
268 ::FreeLibrary(module);
270 if (NULL == child_var)
271 return SBOX_ERROR_GENERIC;
273 size_t offset = reinterpret_cast<char*>(child_var) -
274 reinterpret_cast<char*>(module);
275 child_var = reinterpret_cast<char*>(MainModule()) + offset;
276 #else
277 UNREFERENCED_PARAMETER(name);
278 #endif
280 SIZE_T written;
281 if (!::WriteProcessMemory(sandbox_process_info_.process_handle(),
282 child_var, address, size, &written))
283 return SBOX_ERROR_GENERIC;
285 if (written != size)
286 return SBOX_ERROR_GENERIC;
288 return SBOX_ALL_OK;
291 // Construct the IPC server and the IPC dispatcher. When the target does
292 // an IPC it will eventually call the dispatcher.
293 DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy,
294 uint32 shared_IPC_size, uint32 shared_policy_size) {
295 // We need to map the shared memory on the target. This is necessary for
296 // any IPC that needs to take place, even if the target has not yet hit
297 // the main( ) function or even has initialized the CRT. So here we set
298 // the handle to the shared section. The target on the first IPC must do
299 // the rest, which boils down to calling MapViewofFile()
301 // We use this single memory pool for IPC and for policy.
302 DWORD shared_mem_size = static_cast<DWORD>(shared_IPC_size +
303 shared_policy_size);
304 shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
305 PAGE_READWRITE | SEC_COMMIT,
306 0, shared_mem_size, NULL));
307 if (!shared_section_.IsValid()) {
308 return ::GetLastError();
311 DWORD access = FILE_MAP_READ | FILE_MAP_WRITE;
312 HANDLE target_shared_section;
313 if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_.Get(),
314 sandbox_process_info_.process_handle(),
315 &target_shared_section, access, FALSE, 0)) {
316 return ::GetLastError();
319 void* shared_memory = ::MapViewOfFile(shared_section_.Get(),
320 FILE_MAP_WRITE|FILE_MAP_READ,
321 0, 0, 0);
322 if (NULL == shared_memory) {
323 return ::GetLastError();
326 CopyPolicyToTarget(policy, shared_policy_size,
327 reinterpret_cast<char*>(shared_memory) + shared_IPC_size);
329 ResultCode ret;
330 // Set the global variables in the target. These are not used on the broker.
331 g_shared_section = target_shared_section;
332 ret = TransferVariable("g_shared_section", &g_shared_section,
333 sizeof(g_shared_section));
334 g_shared_section = NULL;
335 if (SBOX_ALL_OK != ret) {
336 return (SBOX_ERROR_GENERIC == ret)?
337 ::GetLastError() : ERROR_INVALID_FUNCTION;
339 g_shared_IPC_size = shared_IPC_size;
340 ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size,
341 sizeof(g_shared_IPC_size));
342 g_shared_IPC_size = 0;
343 if (SBOX_ALL_OK != ret) {
344 return (SBOX_ERROR_GENERIC == ret) ?
345 ::GetLastError() : ERROR_INVALID_FUNCTION;
347 g_shared_policy_size = shared_policy_size;
348 ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size,
349 sizeof(g_shared_policy_size));
350 g_shared_policy_size = 0;
351 if (SBOX_ALL_OK != ret) {
352 return (SBOX_ERROR_GENERIC == ret) ?
353 ::GetLastError() : ERROR_INVALID_FUNCTION;
356 ipc_server_.reset(
357 new SharedMemIPCServer(sandbox_process_info_.process_handle(),
358 sandbox_process_info_.process_id(),
359 thread_pool_, ipc_dispatcher));
361 if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize))
362 return ERROR_NOT_ENOUGH_MEMORY;
364 // After this point we cannot use this handle anymore.
365 ::CloseHandle(sandbox_process_info_.TakeThreadHandle());
367 return ERROR_SUCCESS;
370 void TargetProcess::Terminate() {
371 if (!sandbox_process_info_.IsValid())
372 return;
374 ::TerminateProcess(sandbox_process_info_.process_handle(), 0);
377 TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) {
378 TargetProcess* target = new TargetProcess(base::win::ScopedHandle(),
379 base::win::ScopedHandle(),
380 NULL, NULL);
381 PROCESS_INFORMATION process_info = {};
382 process_info.hProcess = process;
383 target->sandbox_process_info_.Set(process_info);
384 target->base_address_ = base_address;
385 return target;
388 } // namespace sandbox