Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / sandbox / win / src / target_process.cc
blob2c5bf3b1f9736975baa9cadf3a558c01873a9c3a
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"
18 namespace {
20 void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
21 if (!source || !size)
22 return;
23 memcpy(dest, source, size);
24 sandbox::PolicyGlobal* policy =
25 reinterpret_cast<sandbox::PolicyGlobal*>(dest);
27 size_t offset = reinterpret_cast<size_t>(source);
29 for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) {
30 size_t buffer = reinterpret_cast<size_t>(policy->entry[i]);
31 if (buffer) {
32 buffer -= offset;
33 policy->entry[i] = reinterpret_cast<sandbox::PolicyBuffer*>(buffer);
40 namespace sandbox {
42 SANDBOX_INTERCEPT HANDLE g_shared_section;
43 SANDBOX_INTERCEPT size_t g_shared_IPC_size;
44 SANDBOX_INTERCEPT size_t g_shared_policy_size;
46 // Returns the address of the main exe module in memory taking in account
47 // address space layout randomization.
48 void* GetBaseAddress(const wchar_t* exe_name, void* entry_point) {
49 HMODULE exe = ::LoadLibrary(exe_name);
50 if (NULL == exe)
51 return exe;
53 base::win::PEImage pe(exe);
54 if (!pe.VerifyMagic()) {
55 ::FreeLibrary(exe);
56 return exe;
58 PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders();
59 char* base = reinterpret_cast<char*>(entry_point) -
60 nt_header->OptionalHeader.AddressOfEntryPoint;
62 ::FreeLibrary(exe);
63 return base;
67 TargetProcess::TargetProcess(HANDLE initial_token, HANDLE lockdown_token,
68 HANDLE job, ThreadProvider* thread_pool)
69 // This object owns everything initialized here except thread_pool and
70 // the job_ handle. The Job handle is closed by BrokerServices and results
71 // eventually in a call to our dtor.
72 : lockdown_token_(lockdown_token),
73 initial_token_(initial_token),
74 job_(job),
75 thread_pool_(thread_pool),
76 base_address_(NULL) {
79 TargetProcess::~TargetProcess() {
80 DWORD exit_code = 0;
81 // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
82 // will take effect only when the context changes. As far as the testing went,
83 // this wait was enough to switch context and kill the processes in the job.
84 // If this process is already dead, the function will return without waiting.
85 // TODO(nsylvain): If the process is still alive at the end, we should kill
86 // it. http://b/893891
87 // For now, this wait is there only to do a best effort to prevent some leaks
88 // from showing up in purify.
89 if (sandbox_process_info_.IsValid()) {
90 ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50);
91 // At this point, the target process should have been killed. Check.
92 if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(),
93 &exit_code) || (STILL_ACTIVE == exit_code)) {
94 // Something went wrong. We don't know if the target is in a state where
95 // it can manage to do another IPC call. If it can, and we've destroyed
96 // the |ipc_server_|, it will crash the broker. So we intentionally leak
97 // that.
98 if (shared_section_.IsValid())
99 shared_section_.Take();
100 ipc_server_.release();
101 sandbox_process_info_.TakeProcessHandle();
102 return;
106 // ipc_server_ references our process handle, so make sure the former is shut
107 // down before the latter is closed (by ScopedProcessInformation).
108 ipc_server_.reset();
111 // Creates the target (child) process suspended and assigns it to the job
112 // object.
113 DWORD TargetProcess::Create(const wchar_t* exe_path,
114 const wchar_t* command_line,
115 bool inherit_handles,
116 const base::win::StartupInformation& startup_info,
117 base::win::ScopedProcessInformation* target_info) {
118 exe_name_.reset(_wcsdup(exe_path));
120 // the command line needs to be writable by CreateProcess().
121 scoped_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line));
123 // Start the target process suspended.
124 DWORD flags =
125 CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
127 if (startup_info.has_extended_startup_info())
128 flags |= EXTENDED_STARTUPINFO_PRESENT;
130 if (job_ && base::win::GetVersion() < base::win::VERSION_WIN8) {
131 // Windows 8 implements nested jobs, but for older systems we need to
132 // break out of any job we're in to enforce our restrictions.
133 flags |= CREATE_BREAKAWAY_FROM_JOB;
136 PROCESS_INFORMATION temp_process_info = {};
137 if (!::CreateProcessAsUserW(lockdown_token_.Get(),
138 exe_path,
139 cmd_line.get(),
140 NULL, // No security attribute.
141 NULL, // No thread attribute.
142 inherit_handles,
143 flags,
144 NULL, // Use the environment of the caller.
145 NULL, // Use current directory of the caller.
146 startup_info.startup_info(),
147 &temp_process_info)) {
148 return ::GetLastError();
150 base::win::ScopedProcessInformation process_info(temp_process_info);
151 lockdown_token_.Close();
153 DWORD win_result = ERROR_SUCCESS;
155 if (job_) {
156 // Assign the suspended target to the windows job object.
157 if (!::AssignProcessToJobObject(job_, process_info.process_handle())) {
158 win_result = ::GetLastError();
159 ::TerminateProcess(process_info.process_handle(), 0);
160 return win_result;
164 if (initial_token_.IsValid()) {
165 // Change the token of the main thread of the new process for the
166 // impersonation token with more rights. This allows the target to start;
167 // otherwise it will crash too early for us to help.
168 HANDLE temp_thread = process_info.thread_handle();
169 if (!::SetThreadToken(&temp_thread, initial_token_.Get())) {
170 win_result = ::GetLastError();
171 // It might be a security breach if we let the target run outside the job
172 // so kill it before it causes damage.
173 ::TerminateProcess(process_info.process_handle(), 0);
174 return win_result;
176 initial_token_.Close();
179 CONTEXT context;
180 context.ContextFlags = CONTEXT_ALL;
181 if (!::GetThreadContext(process_info.thread_handle(), &context)) {
182 win_result = ::GetLastError();
183 ::TerminateProcess(process_info.process_handle(), 0);
184 return win_result;
187 #if defined(_WIN64)
188 void* entry_point = reinterpret_cast<void*>(context.Rcx);
189 #else
190 #pragma warning(push)
191 #pragma warning(disable: 4312)
192 // This cast generates a warning because it is 32 bit specific.
193 void* entry_point = reinterpret_cast<void*>(context.Eax);
194 #pragma warning(pop)
195 #endif // _WIN64
197 if (!target_info->DuplicateFrom(process_info)) {
198 win_result = ::GetLastError(); // This may or may not be correct.
199 ::TerminateProcess(process_info.process_handle(), 0);
200 return win_result;
203 base_address_ = GetBaseAddress(exe_path, entry_point);
204 sandbox_process_info_.Set(process_info.Take());
205 return win_result;
208 ResultCode TargetProcess::TransferVariable(const char* name, void* address,
209 size_t size) {
210 if (!sandbox_process_info_.IsValid())
211 return SBOX_ERROR_UNEXPECTED_CALL;
213 void* child_var = address;
215 #if SANDBOX_EXPORTS
216 HMODULE module = ::LoadLibrary(exe_name_.get());
217 if (NULL == module)
218 return SBOX_ERROR_GENERIC;
220 child_var = ::GetProcAddress(module, name);
221 ::FreeLibrary(module);
223 if (NULL == child_var)
224 return SBOX_ERROR_GENERIC;
226 size_t offset = reinterpret_cast<char*>(child_var) -
227 reinterpret_cast<char*>(module);
228 child_var = reinterpret_cast<char*>(MainModule()) + offset;
229 #else
230 UNREFERENCED_PARAMETER(name);
231 #endif
233 SIZE_T written;
234 if (!::WriteProcessMemory(sandbox_process_info_.process_handle(),
235 child_var, address, size, &written))
236 return SBOX_ERROR_GENERIC;
238 if (written != size)
239 return SBOX_ERROR_GENERIC;
241 return SBOX_ALL_OK;
244 // Construct the IPC server and the IPC dispatcher. When the target does
245 // an IPC it will eventually call the dispatcher.
246 DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy,
247 uint32 shared_IPC_size, uint32 shared_policy_size) {
248 // We need to map the shared memory on the target. This is necessary for
249 // any IPC that needs to take place, even if the target has not yet hit
250 // the main( ) function or even has initialized the CRT. So here we set
251 // the handle to the shared section. The target on the first IPC must do
252 // the rest, which boils down to calling MapViewofFile()
254 // We use this single memory pool for IPC and for policy.
255 DWORD shared_mem_size = static_cast<DWORD>(shared_IPC_size +
256 shared_policy_size);
257 shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
258 PAGE_READWRITE | SEC_COMMIT,
259 0, shared_mem_size, NULL));
260 if (!shared_section_.IsValid()) {
261 return ::GetLastError();
264 DWORD access = FILE_MAP_READ | FILE_MAP_WRITE;
265 HANDLE target_shared_section;
266 if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_.Get(),
267 sandbox_process_info_.process_handle(),
268 &target_shared_section, access, FALSE, 0)) {
269 return ::GetLastError();
272 void* shared_memory = ::MapViewOfFile(shared_section_.Get(),
273 FILE_MAP_WRITE|FILE_MAP_READ,
274 0, 0, 0);
275 if (NULL == shared_memory) {
276 return ::GetLastError();
279 CopyPolicyToTarget(policy, shared_policy_size,
280 reinterpret_cast<char*>(shared_memory) + shared_IPC_size);
282 ResultCode ret;
283 // Set the global variables in the target. These are not used on the broker.
284 g_shared_section = target_shared_section;
285 ret = TransferVariable("g_shared_section", &g_shared_section,
286 sizeof(g_shared_section));
287 g_shared_section = NULL;
288 if (SBOX_ALL_OK != ret) {
289 return (SBOX_ERROR_GENERIC == ret)?
290 ::GetLastError() : ERROR_INVALID_FUNCTION;
292 g_shared_IPC_size = shared_IPC_size;
293 ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size,
294 sizeof(g_shared_IPC_size));
295 g_shared_IPC_size = 0;
296 if (SBOX_ALL_OK != ret) {
297 return (SBOX_ERROR_GENERIC == ret) ?
298 ::GetLastError() : ERROR_INVALID_FUNCTION;
300 g_shared_policy_size = shared_policy_size;
301 ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size,
302 sizeof(g_shared_policy_size));
303 g_shared_policy_size = 0;
304 if (SBOX_ALL_OK != ret) {
305 return (SBOX_ERROR_GENERIC == ret) ?
306 ::GetLastError() : ERROR_INVALID_FUNCTION;
309 ipc_server_.reset(
310 new SharedMemIPCServer(sandbox_process_info_.process_handle(),
311 sandbox_process_info_.process_id(),
312 job_, thread_pool_, ipc_dispatcher));
314 if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize))
315 return ERROR_NOT_ENOUGH_MEMORY;
317 // After this point we cannot use this handle anymore.
318 ::CloseHandle(sandbox_process_info_.TakeThreadHandle());
320 return ERROR_SUCCESS;
323 void TargetProcess::Terminate() {
324 if (!sandbox_process_info_.IsValid())
325 return;
327 ::TerminateProcess(sandbox_process_info_.process_handle(), 0);
330 TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) {
331 TargetProcess* target = new TargetProcess(NULL, NULL, NULL, NULL);
332 PROCESS_INFORMATION process_info = {};
333 process_info.hProcess = process;
334 target->sandbox_process_info_.Set(process_info);
335 target->base_address_ = base_address;
336 return target;
339 } // namespace sandbox