Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(88)

Side by Side Diff: sandbox/src/target_process.cc

Issue 10689170: Move the Windows sandbox to sandbox/win (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase on top of tree (properly this time) Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sandbox/src/target_process.h ('k') | sandbox/src/target_services.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "sandbox/src/target_process.h"
6
7 #include "base/basictypes.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/win/pe_image.h"
10 #include "base/win/windows_version.h"
11 #include "sandbox/src/crosscall_server.h"
12 #include "sandbox/src/crosscall_client.h"
13 #include "sandbox/src/policy_low_level.h"
14 #include "sandbox/src/sandbox_types.h"
15 #include "sandbox/src/sharedmem_ipc_server.h"
16
17 namespace {
18
19 void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
20 if (!source || !size)
21 return;
22 memcpy(dest, source, size);
23 sandbox::PolicyGlobal* policy =
24 reinterpret_cast<sandbox::PolicyGlobal*>(dest);
25
26 size_t offset = reinterpret_cast<size_t>(source);
27
28 for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) {
29 size_t buffer = reinterpret_cast<size_t>(policy->entry[i]);
30 if (buffer) {
31 buffer -= offset;
32 policy->entry[i] = reinterpret_cast<sandbox::PolicyBuffer*>(buffer);
33 }
34 }
35 }
36
37 // Reserve a random range at the bottom of the address space in the target
38 // process to prevent predictable alocations at low addresses.
39 void PoisonLowerAddressRange(HANDLE process) {
40 unsigned int limit;
41 rand_s(&limit);
42 char* ptr = 0;
43 const size_t kMask64k = 0xFFFF;
44 // Random range (512k-16.5mb) in 64k steps.
45 const char* end = ptr + ((((limit % 16384) + 512) * 1024) & ~kMask64k);
46 while (ptr < end) {
47 MEMORY_BASIC_INFORMATION memory_info;
48 if (!::VirtualQueryEx(process, ptr, &memory_info, sizeof(memory_info)))
49 break;
50 size_t size = std::min((memory_info.RegionSize + kMask64k) & ~kMask64k,
51 static_cast<SIZE_T>(end - ptr));
52 if (ptr && memory_info.State == MEM_FREE)
53 ::VirtualAllocEx(process, ptr, size, MEM_RESERVE, PAGE_NOACCESS);
54 ptr += size;
55 }
56 }
57
58 }
59
60 namespace sandbox {
61
62 SANDBOX_INTERCEPT HANDLE g_shared_section;
63 SANDBOX_INTERCEPT size_t g_shared_IPC_size;
64 SANDBOX_INTERCEPT size_t g_shared_policy_size;
65
66 // Returns the address of the main exe module in memory taking in account
67 // address space layout randomization.
68 void* GetBaseAddress(const wchar_t* exe_name, void* entry_point) {
69 HMODULE exe = ::LoadLibrary(exe_name);
70 if (NULL == exe)
71 return exe;
72
73 base::win::PEImage pe(exe);
74 if (!pe.VerifyMagic()) {
75 ::FreeLibrary(exe);
76 return exe;
77 }
78 PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders();
79 char* base = reinterpret_cast<char*>(entry_point) -
80 nt_header->OptionalHeader.AddressOfEntryPoint;
81
82 ::FreeLibrary(exe);
83 return base;
84 }
85
86
87 TargetProcess::TargetProcess(HANDLE initial_token, HANDLE lockdown_token,
88 HANDLE job, ThreadProvider* thread_pool)
89 // This object owns everything initialized here except thread_pool and
90 // the job_ handle. The Job handle is closed by BrokerServices and results
91 // eventually in a call to our dtor.
92 : lockdown_token_(lockdown_token),
93 initial_token_(initial_token),
94 job_(job),
95 thread_pool_(thread_pool),
96 base_address_(NULL) {
97 }
98
99 TargetProcess::~TargetProcess() {
100 DWORD exit_code = 0;
101 // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
102 // will take effect only when the context changes. As far as the testing went,
103 // this wait was enough to switch context and kill the processes in the job.
104 // If this process is already dead, the function will return without waiting.
105 // TODO(nsylvain): If the process is still alive at the end, we should kill
106 // it. http://b/893891
107 // For now, this wait is there only to do a best effort to prevent some leaks
108 // from showing up in purify.
109 ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50);
110 if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(),
111 &exit_code) || (STILL_ACTIVE == exit_code)) {
112 // It is an error to destroy this object while the target process is still
113 // alive because we need to destroy the IPC subsystem and cannot risk to
114 // have an IPC reach us after this point.
115 shared_section_.Take();
116 SharedMemIPCServer* server = ipc_server_.release();
117 sandbox_process_info_.TakeProcessHandle();
118 return;
119 }
120
121 // ipc_server_ references our process handle, so make sure the former is shut
122 // down before the latter is closed (by ScopedProcessInformation).
123 ipc_server_.reset();
124 }
125
126 // Creates the target (child) process suspended and assigns it to the job
127 // object.
128 DWORD TargetProcess::Create(const wchar_t* exe_path,
129 const wchar_t* command_line,
130 const wchar_t* desktop,
131 base::win::ScopedProcessInformation* target_info) {
132 exe_name_.reset(_wcsdup(exe_path));
133
134 // the command line needs to be writable by CreateProcess().
135 scoped_ptr_malloc<wchar_t> cmd_line(_wcsdup(command_line));
136 scoped_ptr_malloc<wchar_t> desktop_name(desktop ? _wcsdup(desktop) : NULL);
137
138 // Start the target process suspended.
139 DWORD flags =
140 CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
141
142 if (base::win::GetVersion() < base::win::VERSION_WIN8) {
143 // Windows 8 implements nested jobs, but for older systems we need to
144 // break out of any job we're in to enforce our restrictions.
145 flags |= CREATE_BREAKAWAY_FROM_JOB;
146 }
147
148 STARTUPINFO startup_info = {sizeof(STARTUPINFO)};
149 if (desktop) {
150 startup_info.lpDesktop = desktop_name.get();
151 }
152
153 base::win::ScopedProcessInformation process_info;
154
155 if (!::CreateProcessAsUserW(lockdown_token_,
156 exe_path,
157 cmd_line.get(),
158 NULL, // No security attribute.
159 NULL, // No thread attribute.
160 FALSE, // Do not inherit handles.
161 flags,
162 NULL, // Use the environment of the caller.
163 NULL, // Use current directory of the caller.
164 &startup_info,
165 process_info.Receive())) {
166 return ::GetLastError();
167 }
168 lockdown_token_.Close();
169
170 PoisonLowerAddressRange(process_info.process_handle());
171
172 DWORD win_result = ERROR_SUCCESS;
173
174 // Assign the suspended target to the windows job object
175 if (!::AssignProcessToJobObject(job_, process_info.process_handle())) {
176 win_result = ::GetLastError();
177 // It might be a security breach if we let the target run outside the job
178 // so kill it before it causes damage
179 ::TerminateProcess(process_info.process_handle(), 0);
180 return win_result;
181 }
182
183 // Change the token of the main thread of the new process for the
184 // impersonation token with more rights. This allows the target to start;
185 // otherwise it will crash too early for us to help.
186 {
187 HANDLE temp_thread = process_info.thread_handle();
188 if (!::SetThreadToken(&temp_thread, initial_token_)) {
189 win_result = ::GetLastError();
190 ::TerminateProcess(process_info.process_handle(), 0);
191 return win_result;
192 }
193 initial_token_.Close();
194 }
195
196 CONTEXT context;
197 context.ContextFlags = CONTEXT_ALL;
198 if (!::GetThreadContext(process_info.thread_handle(), &context)) {
199 win_result = ::GetLastError();
200 ::TerminateProcess(process_info.process_handle(), 0);
201 return win_result;
202 }
203
204 #if defined(_WIN64)
205 void* entry_point = reinterpret_cast<void*>(context.Rcx);
206 #else
207 #pragma warning(push)
208 #pragma warning(disable: 4312)
209 // This cast generates a warning because it is 32 bit specific.
210 void* entry_point = reinterpret_cast<void*>(context.Eax);
211 #pragma warning(pop)
212 #endif // _WIN64
213
214 if (!target_info->DuplicateFrom(process_info)) {
215 win_result = ::GetLastError(); // This may or may not be correct.
216 ::TerminateProcess(process_info.process_handle(), 0);
217 return win_result;
218 }
219
220 base_address_ = GetBaseAddress(exe_path, entry_point);
221 sandbox_process_info_.Swap(&process_info);
222 return win_result;
223 }
224
225 ResultCode TargetProcess::TransferVariable(const char* name, void* address,
226 size_t size) {
227 if (!sandbox_process_info_.IsValid())
228 return SBOX_ERROR_UNEXPECTED_CALL;
229
230 void* child_var = address;
231
232 #if SANDBOX_EXPORTS
233 HMODULE module = ::LoadLibrary(exe_name_.get());
234 if (NULL == module)
235 return SBOX_ERROR_GENERIC;
236
237 child_var = ::GetProcAddress(module, name);
238 ::FreeLibrary(module);
239
240 if (NULL == child_var)
241 return SBOX_ERROR_GENERIC;
242
243 size_t offset = reinterpret_cast<char*>(child_var) -
244 reinterpret_cast<char*>(module);
245 child_var = reinterpret_cast<char*>(MainModule()) + offset;
246 #else
247 UNREFERENCED_PARAMETER(name);
248 #endif
249
250 SIZE_T written;
251 if (!::WriteProcessMemory(sandbox_process_info_.process_handle(),
252 child_var, address, size, &written))
253 return SBOX_ERROR_GENERIC;
254
255 if (written != size)
256 return SBOX_ERROR_GENERIC;
257
258 return SBOX_ALL_OK;
259 }
260
261 // Construct the IPC server and the IPC dispatcher. When the target does
262 // an IPC it will eventually call the dispatcher.
263 DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy,
264 uint32 shared_IPC_size, uint32 shared_policy_size) {
265 // We need to map the shared memory on the target. This is necessary for
266 // any IPC that needs to take place, even if the target has not yet hit
267 // the main( ) function or even has initialized the CRT. So here we set
268 // the handle to the shared section. The target on the first IPC must do
269 // the rest, which boils down to calling MapViewofFile()
270
271 // We use this single memory pool for IPC and for policy.
272 DWORD shared_mem_size = static_cast<DWORD>(shared_IPC_size +
273 shared_policy_size);
274 shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
275 PAGE_READWRITE | SEC_COMMIT,
276 0, shared_mem_size, NULL));
277 if (!shared_section_.IsValid()) {
278 return ::GetLastError();
279 }
280
281 DWORD access = FILE_MAP_READ | FILE_MAP_WRITE;
282 HANDLE target_shared_section;
283 if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_,
284 sandbox_process_info_.process_handle(),
285 &target_shared_section, access, FALSE, 0)) {
286 return ::GetLastError();
287 }
288
289 void* shared_memory = ::MapViewOfFile(shared_section_,
290 FILE_MAP_WRITE|FILE_MAP_READ,
291 0, 0, 0);
292 if (NULL == shared_memory) {
293 return ::GetLastError();
294 }
295
296 CopyPolicyToTarget(policy, shared_policy_size,
297 reinterpret_cast<char*>(shared_memory) + shared_IPC_size);
298
299 ResultCode ret;
300 // Set the global variables in the target. These are not used on the broker.
301 g_shared_section = target_shared_section;
302 ret = TransferVariable("g_shared_section", &g_shared_section,
303 sizeof(g_shared_section));
304 g_shared_section = NULL;
305 if (SBOX_ALL_OK != ret) {
306 return (SBOX_ERROR_GENERIC == ret)?
307 ::GetLastError() : ERROR_INVALID_FUNCTION;
308 }
309 g_shared_IPC_size = shared_IPC_size;
310 ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size,
311 sizeof(g_shared_IPC_size));
312 g_shared_IPC_size = 0;
313 if (SBOX_ALL_OK != ret) {
314 return (SBOX_ERROR_GENERIC == ret) ?
315 ::GetLastError() : ERROR_INVALID_FUNCTION;
316 }
317 g_shared_policy_size = shared_policy_size;
318 ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size,
319 sizeof(g_shared_policy_size));
320 g_shared_policy_size = 0;
321 if (SBOX_ALL_OK != ret) {
322 return (SBOX_ERROR_GENERIC == ret) ?
323 ::GetLastError() : ERROR_INVALID_FUNCTION;
324 }
325
326 ipc_server_.reset(
327 new SharedMemIPCServer(sandbox_process_info_.process_handle(),
328 sandbox_process_info_.process_id(),
329 job_, thread_pool_, ipc_dispatcher));
330
331 if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize))
332 return ERROR_NOT_ENOUGH_MEMORY;
333
334 // After this point we cannot use this handle anymore.
335 ::CloseHandle(sandbox_process_info_.TakeThreadHandle());
336
337 return ERROR_SUCCESS;
338 }
339
340 void TargetProcess::Terminate() {
341 if (!sandbox_process_info_.IsValid())
342 return;
343
344 ::TerminateProcess(sandbox_process_info_.process_handle(), 0);
345 }
346
347
348 TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) {
349 TargetProcess* target = new TargetProcess(NULL, NULL, NULL, NULL);
350 target->sandbox_process_info_.Receive()->hProcess = process;
351 target->base_address_ = base_address;
352 return target;
353 }
354
355 } // namespace sandbox
OLDNEW
« no previous file with comments | « sandbox/src/target_process.h ('k') | sandbox/src/target_services.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698