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

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

Issue 10783004: Move Windows Sandbox, trybots version (don't commit me!) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase to top of tree 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/sandbox_nt_util.h ('k') | sandbox/src/sandbox_policy.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/sandbox_nt_util.h"
6
7 #include "base/win/pe_image.h"
8 #include "sandbox/src/sandbox_factory.h"
9 #include "sandbox/src/target_services.h"
10
11 namespace sandbox {
12
13 // This is the list of all imported symbols from ntdll.dll.
14 SANDBOX_INTERCEPT NtExports g_nt = { NULL };
15
16 }
17
18 namespace {
19
20 #if defined(_WIN64)
21 void* AllocateNearTo(void* source, size_t size) {
22 using sandbox::g_nt;
23
24 // Start with 1 GB above the source.
25 const unsigned int kOneGB = 0x40000000;
26 void* base = reinterpret_cast<char*>(source) + kOneGB;
27 SIZE_T actual_size = size;
28 ULONG_PTR zero_bits = 0; // Not the correct type if used.
29 ULONG type = MEM_RESERVE;
30
31 if (reinterpret_cast<SIZE_T>(source) > 0x7ff80000000) {
32 // We are at the top of the address space. Let's try the highest available
33 // address.
34 base = NULL;
35 type |= MEM_TOP_DOWN;
36 }
37
38 NTSTATUS ret;
39 int attempts = 0;
40 for (; attempts < 20; attempts++) {
41 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
42 &actual_size, type, PAGE_READWRITE);
43 if (NT_SUCCESS(ret)) {
44 if (base < source) {
45 // We won't be able to patch this dll.
46 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
47 MEM_RELEASE));
48 return NULL;
49 }
50 break;
51 }
52
53 // Try 100 MB higher.
54 base = reinterpret_cast<char*>(base) + 100 * 0x100000;
55 };
56
57 if (attempts == 20)
58 return NULL;
59
60 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
61 &actual_size, MEM_COMMIT, PAGE_READWRITE);
62
63 if (!NT_SUCCESS(ret)) {
64 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
65 MEM_RELEASE));
66 base = NULL;
67 }
68
69 return base;
70 }
71 #else // defined(_WIN64).
72 void* AllocateNearTo(void* source, size_t size) {
73 using sandbox::g_nt;
74 UNREFERENCED_PARAMETER(source);
75
76 // In 32-bit processes allocations below 512k are predictable, so mark
77 // anything in that range as reserved and retry until we get a good address.
78 const void* const kMinAddress = reinterpret_cast<void*>(512 * 1024);
79 NTSTATUS ret;
80 SIZE_T actual_size;
81 void* base;
82 do {
83 base = NULL;
84 actual_size = 64 * 1024;
85 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
86 MEM_RESERVE, PAGE_NOACCESS);
87 if (!NT_SUCCESS(ret))
88 return NULL;
89 } while (base < kMinAddress);
90
91 actual_size = size;
92 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
93 MEM_COMMIT, PAGE_READWRITE);
94 if (!NT_SUCCESS(ret))
95 return NULL;
96 return base;
97 }
98 #endif // defined(_WIN64).
99
100 } // namespace.
101
102 namespace sandbox {
103
104 // Handle for our private heap.
105 void* g_heap = NULL;
106
107 SANDBOX_INTERCEPT HANDLE g_shared_section;
108 SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0;
109 SANDBOX_INTERCEPT size_t g_shared_policy_size = 0;
110
111 void* volatile g_shared_policy_memory = NULL;
112 void* volatile g_shared_IPC_memory = NULL;
113
114 // Both the IPC and the policy share a single region of memory in which the IPC
115 // memory is first and the policy memory is last.
116 bool MapGlobalMemory() {
117 if (NULL == g_shared_IPC_memory) {
118 void* memory = NULL;
119 SIZE_T size = 0;
120 // Map the entire shared section from the start.
121 NTSTATUS ret = g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess,
122 &memory, 0, 0, NULL, &size, ViewUnmap,
123 0, PAGE_READWRITE);
124
125 if (!NT_SUCCESS(ret) || NULL == memory) {
126 NOTREACHED_NT();
127 return false;
128 }
129
130 if (NULL != _InterlockedCompareExchangePointer(&g_shared_IPC_memory,
131 memory, NULL)) {
132 // Somebody beat us to the memory setup.
133 ret = g_nt.UnmapViewOfSection(NtCurrentProcess, memory);
134 VERIFY_SUCCESS(ret);
135 }
136 DCHECK_NT(g_shared_IPC_size > 0);
137 g_shared_policy_memory = reinterpret_cast<char*>(g_shared_IPC_memory)
138 + g_shared_IPC_size;
139 }
140 DCHECK_NT(g_shared_policy_memory);
141 DCHECK_NT(g_shared_policy_size > 0);
142 return true;
143 }
144
145 void* GetGlobalIPCMemory() {
146 if (!MapGlobalMemory())
147 return NULL;
148 return g_shared_IPC_memory;
149 }
150
151 void* GetGlobalPolicyMemory() {
152 if (!MapGlobalMemory())
153 return NULL;
154 return g_shared_policy_memory;
155 }
156
157 bool InitHeap() {
158 if (!g_heap) {
159 // Create a new heap using default values for everything.
160 void* heap = g_nt.RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL);
161 if (!heap)
162 return false;
163
164 if (NULL != _InterlockedCompareExchangePointer(&g_heap, heap, NULL)) {
165 // Somebody beat us to the memory setup.
166 g_nt.RtlDestroyHeap(heap);
167 }
168 }
169 return (g_heap) ? true : false;
170 }
171
172 // Physically reads or writes from memory to verify that (at this time), it is
173 // valid. Returns a dummy value.
174 int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) {
175 const int kPageSize = 4096;
176 int dummy = 0;
177 char* start = reinterpret_cast<char*>(buffer);
178 char* end = start + size_bytes - 1;
179
180 if (WRITE == intent) {
181 for (; start < end; start += kPageSize) {
182 *start = 0;
183 }
184 *end = 0;
185 } else {
186 for (; start < end; start += kPageSize) {
187 dummy += *start;
188 }
189 dummy += *end;
190 }
191
192 return dummy;
193 }
194
195 bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) {
196 DCHECK_NT(size);
197 __try {
198 TouchMemory(buffer, size, intent);
199 } __except(EXCEPTION_EXECUTE_HANDLER) {
200 return false;
201 }
202 return true;
203 }
204
205 NTSTATUS CopyData(void* destination, const void* source, size_t bytes) {
206 NTSTATUS ret = STATUS_SUCCESS;
207 __try {
208 if (SandboxFactory::GetTargetServices()->GetState()->InitCalled()) {
209 memcpy(destination, source, bytes);
210 } else {
211 const char* from = reinterpret_cast<const char*>(source);
212 char* to = reinterpret_cast<char*>(destination);
213 for (size_t i = 0; i < bytes; i++) {
214 to[i] = from[i];
215 }
216 }
217 } __except(EXCEPTION_EXECUTE_HANDLER) {
218 ret = GetExceptionCode();
219 }
220 return ret;
221 }
222
223 // Hacky code... replace with AllocAndCopyObjectAttributes.
224 NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
225 wchar_t** out_name, uint32* attributes,
226 HANDLE* root) {
227 if (!InitHeap())
228 return STATUS_NO_MEMORY;
229
230 DCHECK_NT(out_name);
231 *out_name = NULL;
232 NTSTATUS ret = STATUS_UNSUCCESSFUL;
233 __try {
234 do {
235 if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root)
236 break;
237 if (NULL == in_object->ObjectName)
238 break;
239 if (NULL == in_object->ObjectName->Buffer)
240 break;
241
242 size_t size = in_object->ObjectName->Length + sizeof(wchar_t);
243 *out_name = new(NT_ALLOC) wchar_t[size/sizeof(wchar_t)];
244 if (NULL == *out_name)
245 break;
246
247 ret = CopyData(*out_name, in_object->ObjectName->Buffer,
248 size - sizeof(wchar_t));
249 if (!NT_SUCCESS(ret))
250 break;
251
252 (*out_name)[size / sizeof(wchar_t) - 1] = L'\0';
253
254 if (attributes)
255 *attributes = in_object->Attributes;
256
257 if (root)
258 *root = in_object->RootDirectory;
259 ret = STATUS_SUCCESS;
260 } while (false);
261 } __except(EXCEPTION_EXECUTE_HANDLER) {
262 ret = GetExceptionCode();
263 }
264
265 if (!NT_SUCCESS(ret) && *out_name) {
266 operator delete(*out_name, NT_ALLOC);
267 *out_name = NULL;
268 }
269
270 return ret;
271 }
272
273 NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) {
274 PROCESS_BASIC_INFORMATION proc_info;
275 ULONG bytes_returned;
276
277 NTSTATUS ret = g_nt.QueryInformationProcess(process, ProcessBasicInformation,
278 &proc_info, sizeof(proc_info),
279 &bytes_returned);
280 if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned)
281 return ret;
282
283 *process_id = proc_info.UniqueProcessId;
284 return STATUS_SUCCESS;
285 }
286
287 bool IsSameProcess(HANDLE process) {
288 if (NtCurrentProcess == process)
289 return true;
290
291 static ULONG s_process_id = 0;
292
293 if (!s_process_id) {
294 NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id);
295 if (!NT_SUCCESS(ret))
296 return false;
297 }
298
299 ULONG process_id;
300 NTSTATUS ret = GetProcessId(process, &process_id);
301 if (!NT_SUCCESS(ret))
302 return false;
303
304 return (process_id == s_process_id);
305 }
306
307 bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset,
308 PSIZE_T view_size) {
309 if (!section || !base || !view_size || offset)
310 return false;
311
312 HANDLE query_section;
313
314 NTSTATUS ret = g_nt.DuplicateObject(NtCurrentProcess, section,
315 NtCurrentProcess, &query_section,
316 SECTION_QUERY, 0, 0);
317 if (!NT_SUCCESS(ret))
318 return false;
319
320 SECTION_BASIC_INFORMATION basic_info;
321 SIZE_T bytes_returned;
322 ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info,
323 sizeof(basic_info), &bytes_returned);
324
325 VERIFY_SUCCESS(g_nt.Close(query_section));
326
327 if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned)
328 return false;
329
330 if (!(basic_info.Attributes & SEC_IMAGE))
331 return false;
332
333 return true;
334 }
335
336 UNICODE_STRING* AnsiToUnicode(const char* string) {
337 ANSI_STRING ansi_string;
338 ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string));
339 ansi_string.MaximumLength = ansi_string.Length + 1;
340 ansi_string.Buffer = const_cast<char*>(string);
341
342 if (ansi_string.Length > ansi_string.MaximumLength)
343 return NULL;
344
345 size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t) +
346 sizeof(UNICODE_STRING);
347
348 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(
349 new(NT_ALLOC) char[name_bytes]);
350 if (!out_string)
351 return NULL;
352
353 out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t);
354 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
355
356 BOOLEAN alloc_destination = FALSE;
357 NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string,
358 alloc_destination);
359 DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret);
360 if (!NT_SUCCESS(ret)) {
361 operator delete(out_string, NT_ALLOC);
362 return NULL;
363 }
364
365 return out_string;
366 }
367
368 UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) {
369 UNICODE_STRING* out_name = NULL;
370 __try {
371 do {
372 *flags = 0;
373 base::win::PEImage pe(module);
374
375 if (!pe.VerifyMagic())
376 break;
377 *flags |= MODULE_IS_PE_IMAGE;
378
379 PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
380 if (exports) {
381 char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name));
382 out_name = AnsiToUnicode(name);
383 }
384
385 PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
386 if (headers) {
387 if (headers->OptionalHeader.AddressOfEntryPoint)
388 *flags |= MODULE_HAS_ENTRY_POINT;
389 if (headers->OptionalHeader.SizeOfCode)
390 *flags |= MODULE_HAS_CODE;
391 }
392 } while (false);
393 } __except(EXCEPTION_EXECUTE_HANDLER) {
394 }
395
396 return out_name;
397 }
398
399 UNICODE_STRING* GetBackingFilePath(PVOID address) {
400 // We'll start with something close to max_path charactes for the name.
401 ULONG buffer_bytes = MAX_PATH * 2;
402
403 for (;;) {
404 MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>(
405 new(NT_ALLOC) char[buffer_bytes]);
406
407 if (!section_name)
408 return NULL;
409
410 ULONG returned_bytes;
411 NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address,
412 MemorySectionName, section_name,
413 buffer_bytes, &returned_bytes);
414
415 if (STATUS_BUFFER_OVERFLOW == ret) {
416 // Retry the call with the given buffer size.
417 operator delete(section_name, NT_ALLOC);
418 section_name = NULL;
419 buffer_bytes = returned_bytes;
420 continue;
421 }
422 if (!NT_SUCCESS(ret)) {
423 operator delete(section_name, NT_ALLOC);
424 return NULL;
425 }
426
427 return reinterpret_cast<UNICODE_STRING*>(section_name);
428 }
429 }
430
431 UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) {
432 if ((!module_path) || (!module_path->Buffer))
433 return NULL;
434
435 wchar_t* sep = NULL;
436 int start_pos = module_path->Length / sizeof(wchar_t) - 1;
437 int ix = start_pos;
438
439 for (; ix >= 0; --ix) {
440 if (module_path->Buffer[ix] == L'\\') {
441 sep = &module_path->Buffer[ix];
442 break;
443 }
444 }
445
446 // Ends with path separator. Not a valid module name.
447 if ((ix == start_pos) && sep)
448 return NULL;
449
450 // No path separator found. Use the entire name.
451 if (!sep) {
452 sep = &module_path->Buffer[-1];
453 }
454
455 // Add one to the size so we can null terminate the string.
456 size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t);
457
458 // Based on the code above, size_bytes should always be small enough
459 // to make the static_cast below safe.
460 DCHECK_NT(kuint16max > size_bytes);
461 char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)];
462 if (!str_buffer)
463 return NULL;
464
465 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer);
466 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
467 out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t));
468 out_string->MaximumLength = static_cast<USHORT>(size_bytes);
469
470 NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length);
471 if (!NT_SUCCESS(ret)) {
472 operator delete(out_string, NT_ALLOC);
473 return NULL;
474 }
475
476 out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0';
477 return out_string;
478 }
479
480 NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes,
481 ULONG protect) {
482 DCHECK_NT(!changed_);
483 SIZE_T new_bytes = bytes;
484 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address,
485 &new_bytes, protect, &old_protect_);
486 if (NT_SUCCESS(ret)) {
487 changed_ = true;
488 address_ = address;
489 bytes_ = new_bytes;
490 }
491
492 return ret;
493 }
494
495 NTSTATUS AutoProtectMemory::RevertProtection() {
496 if (!changed_)
497 return STATUS_SUCCESS;
498
499 DCHECK_NT(address_);
500 DCHECK_NT(bytes_);
501
502 SIZE_T new_bytes = bytes_;
503 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address_,
504 &new_bytes, old_protect_,
505 &old_protect_);
506 DCHECK_NT(NT_SUCCESS(ret));
507
508 changed_ = false;
509 address_ = NULL;
510 bytes_ = 0;
511 old_protect_ = 0;
512
513 return ret;
514 }
515
516 bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length,
517 uint32 file_info_class) {
518 if (FileRenameInformation != file_info_class)
519 return false;
520
521 if (length < sizeof(FILE_RENAME_INFORMATION))
522 return false;
523
524 // Make sure file name length doesn't exceed the message length
525 if (length - offsetof(FILE_RENAME_INFORMATION, FileName) <
526 file_info->FileNameLength)
527 return false;
528
529 // We don't support a root directory.
530 if (file_info->RootDirectory)
531 return false;
532
533 // Check if it starts with \\??\\. We don't support relative paths.
534 if (file_info->FileNameLength < 4 || file_info->FileNameLength > kuint16max)
535 return false;
536
537 if (file_info->FileName[0] != L'\\' ||
538 file_info->FileName[1] != L'?' ||
539 file_info->FileName[2] != L'?' ||
540 file_info->FileName[3] != L'\\')
541 return false;
542
543 return true;
544 }
545
546 } // namespace sandbox
547
548 void* operator new(size_t size, sandbox::AllocationType type,
549 void* near_to) {
550 using namespace sandbox;
551
552 if (NT_ALLOC == type) {
553 if (!InitHeap())
554 return NULL;
555
556 // Use default flags for the allocation.
557 return g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size);
558 } else if (NT_PAGE == type) {
559 return AllocateNearTo(near_to, size);
560 }
561 NOTREACHED_NT();
562 return NULL;
563 }
564
565 void operator delete(void* memory, sandbox::AllocationType type) {
566 using namespace sandbox;
567
568 if (NT_ALLOC == type) {
569 // Use default flags.
570 VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory));
571 } else if (NT_PAGE == type) {
572 void* base = memory;
573 SIZE_T size = 0;
574 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
575 MEM_RELEASE));
576 } else {
577 NOTREACHED_NT();
578 }
579 }
580
581 void operator delete(void* memory, sandbox::AllocationType type,
582 void* near_to) {
583 UNREFERENCED_PARAMETER(near_to);
584 operator delete(memory, type);
585 }
586
587 void* __cdecl operator new(size_t size, void* buffer,
588 sandbox::AllocationType type) {
589 UNREFERENCED_PARAMETER(size);
590 UNREFERENCED_PARAMETER(type);
591 return buffer;
592 }
593
594 void __cdecl operator delete(void* memory, void* buffer,
595 sandbox::AllocationType type) {
596 UNREFERENCED_PARAMETER(memory);
597 UNREFERENCED_PARAMETER(buffer);
598 UNREFERENCED_PARAMETER(type);
599 }
OLDNEW
« no previous file with comments | « sandbox/src/sandbox_nt_util.h ('k') | sandbox/src/sandbox_policy.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698