Add ICU message format support
[chromium-blink-merge.git] / sandbox / win / src / sandbox_nt_util.cc
blob64fd1f1f6d7f2c173b39d476925b2c1e3a90da6a
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/sandbox_nt_util.h"
7 #include "base/win/pe_image.h"
8 #include "sandbox/win/src/sandbox_factory.h"
9 #include "sandbox/win/src/target_services.h"
11 namespace sandbox {
13 // This is the list of all imported symbols from ntdll.dll.
14 SANDBOX_INTERCEPT NtExports g_nt;
16 } // namespace sandbox
18 namespace {
20 #if defined(_WIN64)
21 void* AllocateNearTo(void* source, size_t size) {
22 using sandbox::g_nt;
24 // Start with 1 GB above the source.
25 const size_t 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;
31 NTSTATUS ret;
32 int attempts = 0;
33 for (; attempts < 41; attempts++) {
34 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
35 &actual_size, type, PAGE_READWRITE);
36 if (NT_SUCCESS(ret)) {
37 if (base < source ||
38 base >= reinterpret_cast<char*>(source) + 4 * kOneGB) {
39 // We won't be able to patch this dll.
40 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
41 MEM_RELEASE));
42 return NULL;
44 break;
47 if (attempts == 30) {
48 // Try the first GB.
49 base = reinterpret_cast<char*>(source);
50 } else if (attempts == 40) {
51 // Try the highest available address.
52 base = NULL;
53 type |= MEM_TOP_DOWN;
56 // Try 100 MB higher.
57 base = reinterpret_cast<char*>(base) + 100 * 0x100000;
60 if (attempts == 41)
61 return NULL;
63 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
64 &actual_size, MEM_COMMIT, PAGE_READWRITE);
66 if (!NT_SUCCESS(ret)) {
67 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
68 MEM_RELEASE));
69 base = NULL;
72 return base;
74 #else // defined(_WIN64).
75 void* AllocateNearTo(void* source, size_t size) {
76 using sandbox::g_nt;
77 UNREFERENCED_PARAMETER(source);
79 // In 32-bit processes allocations below 512k are predictable, so mark
80 // anything in that range as reserved and retry until we get a good address.
81 const void* const kMinAddress = reinterpret_cast<void*>(512 * 1024);
82 NTSTATUS ret;
83 SIZE_T actual_size;
84 void* base;
85 do {
86 base = NULL;
87 actual_size = 64 * 1024;
88 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
89 MEM_RESERVE, PAGE_NOACCESS);
90 if (!NT_SUCCESS(ret))
91 return NULL;
92 } while (base < kMinAddress);
94 actual_size = size;
95 ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
96 MEM_COMMIT, PAGE_READWRITE);
97 if (!NT_SUCCESS(ret))
98 return NULL;
99 return base;
101 #endif // defined(_WIN64).
103 } // namespace.
105 namespace sandbox {
107 // Handle for our private heap.
108 void* g_heap = NULL;
110 SANDBOX_INTERCEPT HANDLE g_shared_section;
111 SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0;
112 SANDBOX_INTERCEPT size_t g_shared_policy_size = 0;
114 void* volatile g_shared_policy_memory = NULL;
115 void* volatile g_shared_IPC_memory = NULL;
117 // Both the IPC and the policy share a single region of memory in which the IPC
118 // memory is first and the policy memory is last.
119 bool MapGlobalMemory() {
120 if (NULL == g_shared_IPC_memory) {
121 void* memory = NULL;
122 SIZE_T size = 0;
123 // Map the entire shared section from the start.
124 NTSTATUS ret = g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess,
125 &memory, 0, 0, NULL, &size, ViewUnmap,
126 0, PAGE_READWRITE);
128 if (!NT_SUCCESS(ret) || NULL == memory) {
129 NOTREACHED_NT();
130 return false;
133 if (NULL != _InterlockedCompareExchangePointer(&g_shared_IPC_memory,
134 memory, NULL)) {
135 // Somebody beat us to the memory setup.
136 ret = g_nt.UnmapViewOfSection(NtCurrentProcess, memory);
137 VERIFY_SUCCESS(ret);
139 DCHECK_NT(g_shared_IPC_size > 0);
140 g_shared_policy_memory = reinterpret_cast<char*>(g_shared_IPC_memory)
141 + g_shared_IPC_size;
143 DCHECK_NT(g_shared_policy_memory);
144 DCHECK_NT(g_shared_policy_size > 0);
145 return true;
148 void* GetGlobalIPCMemory() {
149 if (!MapGlobalMemory())
150 return NULL;
151 return g_shared_IPC_memory;
154 void* GetGlobalPolicyMemory() {
155 if (!MapGlobalMemory())
156 return NULL;
157 return g_shared_policy_memory;
160 bool InitHeap() {
161 if (!g_heap) {
162 // Create a new heap using default values for everything.
163 void* heap = g_nt.RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL);
164 if (!heap)
165 return false;
167 if (NULL != _InterlockedCompareExchangePointer(&g_heap, heap, NULL)) {
168 // Somebody beat us to the memory setup.
169 g_nt.RtlDestroyHeap(heap);
172 return (g_heap != NULL);
175 // Physically reads or writes from memory to verify that (at this time), it is
176 // valid. Returns a dummy value.
177 int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) {
178 const int kPageSize = 4096;
179 int dummy = 0;
180 char* start = reinterpret_cast<char*>(buffer);
181 char* end = start + size_bytes - 1;
183 if (WRITE == intent) {
184 for (; start < end; start += kPageSize) {
185 *start = 0;
187 *end = 0;
188 } else {
189 for (; start < end; start += kPageSize) {
190 dummy += *start;
192 dummy += *end;
195 return dummy;
198 bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) {
199 DCHECK_NT(size);
200 __try {
201 TouchMemory(buffer, size, intent);
202 } __except(EXCEPTION_EXECUTE_HANDLER) {
203 return false;
205 return true;
208 NTSTATUS CopyData(void* destination, const void* source, size_t bytes) {
209 NTSTATUS ret = STATUS_SUCCESS;
210 __try {
211 g_nt.memcpy(destination, source, bytes);
212 } __except(EXCEPTION_EXECUTE_HANDLER) {
213 ret = GetExceptionCode();
215 return ret;
218 NTSTATUS AllocAndGetFullPath(HANDLE root,
219 wchar_t* path,
220 wchar_t** full_path) {
221 if (!InitHeap())
222 return STATUS_NO_MEMORY;
224 DCHECK_NT(full_path);
225 DCHECK_NT(path);
226 *full_path = NULL;
227 OBJECT_NAME_INFORMATION* handle_name = NULL;
228 NTSTATUS ret = STATUS_UNSUCCESSFUL;
229 __try {
230 do {
231 static NtQueryObjectFunction NtQueryObject = NULL;
232 if (!NtQueryObject)
233 ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
235 ULONG size = 0;
236 // Query the name information a first time to get the size of the name.
237 ret = NtQueryObject(root, ObjectNameInformation, NULL, 0, &size);
239 if (size) {
240 handle_name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(
241 new(NT_ALLOC) BYTE[size]);
243 // Query the name information a second time to get the name of the
244 // object referenced by the handle.
245 ret = NtQueryObject(root, ObjectNameInformation, handle_name, size,
246 &size);
249 if (STATUS_SUCCESS != ret)
250 break;
252 // Space for path + '\' + name + '\0'.
253 size_t name_length = handle_name->ObjectName.Length +
254 (wcslen(path) + 2) * sizeof(wchar_t);
255 *full_path = new(NT_ALLOC) wchar_t[name_length/sizeof(wchar_t)];
256 if (NULL == *full_path)
257 break;
258 wchar_t* off = *full_path;
259 ret = CopyData(off, handle_name->ObjectName.Buffer,
260 handle_name->ObjectName.Length);
261 if (!NT_SUCCESS(ret))
262 break;
263 off += handle_name->ObjectName.Length / sizeof(wchar_t);
264 *off = L'\\';
265 off += 1;
266 ret = CopyData(off, path, wcslen(path) * sizeof(wchar_t));
267 if (!NT_SUCCESS(ret))
268 break;
269 off += wcslen(path);
270 *off = L'\0';
271 } while (false);
272 } __except(EXCEPTION_EXECUTE_HANDLER) {
273 ret = GetExceptionCode();
276 if (!NT_SUCCESS(ret)) {
277 if (*full_path) {
278 operator delete(*full_path, NT_ALLOC);
279 *full_path = NULL;
281 if (handle_name) {
282 operator delete(handle_name, NT_ALLOC);
283 handle_name = NULL;
287 return ret;
290 // Hacky code... replace with AllocAndCopyObjectAttributes.
291 NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
292 wchar_t** out_name, uint32* attributes,
293 HANDLE* root) {
294 if (!InitHeap())
295 return STATUS_NO_MEMORY;
297 DCHECK_NT(out_name);
298 *out_name = NULL;
299 NTSTATUS ret = STATUS_UNSUCCESSFUL;
300 __try {
301 do {
302 if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root)
303 break;
304 if (NULL == in_object->ObjectName)
305 break;
306 if (NULL == in_object->ObjectName->Buffer)
307 break;
309 size_t size = in_object->ObjectName->Length + sizeof(wchar_t);
310 *out_name = new(NT_ALLOC) wchar_t[size/sizeof(wchar_t)];
311 if (NULL == *out_name)
312 break;
314 ret = CopyData(*out_name, in_object->ObjectName->Buffer,
315 size - sizeof(wchar_t));
316 if (!NT_SUCCESS(ret))
317 break;
319 (*out_name)[size / sizeof(wchar_t) - 1] = L'\0';
321 if (attributes)
322 *attributes = in_object->Attributes;
324 if (root)
325 *root = in_object->RootDirectory;
326 ret = STATUS_SUCCESS;
327 } while (false);
328 } __except(EXCEPTION_EXECUTE_HANDLER) {
329 ret = GetExceptionCode();
332 if (!NT_SUCCESS(ret) && *out_name) {
333 operator delete(*out_name, NT_ALLOC);
334 *out_name = NULL;
337 return ret;
340 NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) {
341 PROCESS_BASIC_INFORMATION proc_info;
342 ULONG bytes_returned;
344 NTSTATUS ret = g_nt.QueryInformationProcess(process, ProcessBasicInformation,
345 &proc_info, sizeof(proc_info),
346 &bytes_returned);
347 if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned)
348 return ret;
350 *process_id = proc_info.UniqueProcessId;
351 return STATUS_SUCCESS;
354 bool IsSameProcess(HANDLE process) {
355 if (NtCurrentProcess == process)
356 return true;
358 static ULONG s_process_id = 0;
360 if (!s_process_id) {
361 NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id);
362 if (!NT_SUCCESS(ret))
363 return false;
366 ULONG process_id;
367 NTSTATUS ret = GetProcessId(process, &process_id);
368 if (!NT_SUCCESS(ret))
369 return false;
371 return (process_id == s_process_id);
374 bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset,
375 PSIZE_T view_size) {
376 if (!section || !base || !view_size || offset)
377 return false;
379 HANDLE query_section;
381 NTSTATUS ret = g_nt.DuplicateObject(NtCurrentProcess, section,
382 NtCurrentProcess, &query_section,
383 SECTION_QUERY, 0, 0);
384 if (!NT_SUCCESS(ret))
385 return false;
387 SECTION_BASIC_INFORMATION basic_info;
388 SIZE_T bytes_returned;
389 ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info,
390 sizeof(basic_info), &bytes_returned);
392 VERIFY_SUCCESS(g_nt.Close(query_section));
394 if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned)
395 return false;
397 if (!(basic_info.Attributes & SEC_IMAGE))
398 return false;
400 return true;
403 UNICODE_STRING* AnsiToUnicode(const char* string) {
404 ANSI_STRING ansi_string;
405 ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string));
406 ansi_string.MaximumLength = ansi_string.Length + 1;
407 ansi_string.Buffer = const_cast<char*>(string);
409 if (ansi_string.Length > ansi_string.MaximumLength)
410 return NULL;
412 size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t) +
413 sizeof(UNICODE_STRING);
415 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(
416 new(NT_ALLOC) char[name_bytes]);
417 if (!out_string)
418 return NULL;
420 out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t);
421 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
423 BOOLEAN alloc_destination = FALSE;
424 NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string,
425 alloc_destination);
426 DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret);
427 if (!NT_SUCCESS(ret)) {
428 operator delete(out_string, NT_ALLOC);
429 return NULL;
432 return out_string;
435 UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) {
436 // PEImage's dtor won't be run during SEH unwinding, but that's OK.
437 #pragma warning(push)
438 #pragma warning(disable: 4509)
439 UNICODE_STRING* out_name = NULL;
440 __try {
441 do {
442 *flags = 0;
443 base::win::PEImage pe(module);
445 if (!pe.VerifyMagic())
446 break;
447 *flags |= MODULE_IS_PE_IMAGE;
449 PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
450 if (exports) {
451 char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name));
452 out_name = AnsiToUnicode(name);
455 PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
456 if (headers) {
457 if (headers->OptionalHeader.AddressOfEntryPoint)
458 *flags |= MODULE_HAS_ENTRY_POINT;
459 if (headers->OptionalHeader.SizeOfCode)
460 *flags |= MODULE_HAS_CODE;
462 } while (false);
463 } __except(EXCEPTION_EXECUTE_HANDLER) {
466 return out_name;
467 #pragma warning(pop)
470 UNICODE_STRING* GetBackingFilePath(PVOID address) {
471 // We'll start with something close to max_path charactes for the name.
472 SIZE_T buffer_bytes = MAX_PATH * 2;
474 for (;;) {
475 MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>(
476 new(NT_ALLOC) char[buffer_bytes]);
478 if (!section_name)
479 return NULL;
481 SIZE_T returned_bytes;
482 NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address,
483 MemorySectionName, section_name,
484 buffer_bytes, &returned_bytes);
486 if (STATUS_BUFFER_OVERFLOW == ret) {
487 // Retry the call with the given buffer size.
488 operator delete(section_name, NT_ALLOC);
489 section_name = NULL;
490 buffer_bytes = returned_bytes;
491 continue;
493 if (!NT_SUCCESS(ret)) {
494 operator delete(section_name, NT_ALLOC);
495 return NULL;
498 return reinterpret_cast<UNICODE_STRING*>(section_name);
502 UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) {
503 if ((!module_path) || (!module_path->Buffer))
504 return NULL;
506 wchar_t* sep = NULL;
507 int start_pos = module_path->Length / sizeof(wchar_t) - 1;
508 int ix = start_pos;
510 for (; ix >= 0; --ix) {
511 if (module_path->Buffer[ix] == L'\\') {
512 sep = &module_path->Buffer[ix];
513 break;
517 // Ends with path separator. Not a valid module name.
518 if ((ix == start_pos) && sep)
519 return NULL;
521 // No path separator found. Use the entire name.
522 if (!sep) {
523 sep = &module_path->Buffer[-1];
526 // Add one to the size so we can null terminate the string.
527 size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t);
529 // Based on the code above, size_bytes should always be small enough
530 // to make the static_cast below safe.
531 DCHECK_NT(kuint16max > size_bytes);
532 char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)];
533 if (!str_buffer)
534 return NULL;
536 UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer);
537 out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
538 out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t));
539 out_string->MaximumLength = static_cast<USHORT>(size_bytes);
541 NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length);
542 if (!NT_SUCCESS(ret)) {
543 operator delete(out_string, NT_ALLOC);
544 return NULL;
547 out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0';
548 return out_string;
551 NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes,
552 ULONG protect) {
553 DCHECK_NT(!changed_);
554 SIZE_T new_bytes = bytes;
555 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address,
556 &new_bytes, protect, &old_protect_);
557 if (NT_SUCCESS(ret)) {
558 changed_ = true;
559 address_ = address;
560 bytes_ = new_bytes;
563 return ret;
566 NTSTATUS AutoProtectMemory::RevertProtection() {
567 if (!changed_)
568 return STATUS_SUCCESS;
570 DCHECK_NT(address_);
571 DCHECK_NT(bytes_);
573 SIZE_T new_bytes = bytes_;
574 NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address_,
575 &new_bytes, old_protect_,
576 &old_protect_);
577 DCHECK_NT(NT_SUCCESS(ret));
579 changed_ = false;
580 address_ = NULL;
581 bytes_ = 0;
582 old_protect_ = 0;
584 return ret;
587 bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length,
588 uint32 file_info_class) {
589 if (FileRenameInformation != file_info_class)
590 return false;
592 if (length < sizeof(FILE_RENAME_INFORMATION))
593 return false;
595 // Make sure file name length doesn't exceed the message length
596 if (length - offsetof(FILE_RENAME_INFORMATION, FileName) <
597 file_info->FileNameLength)
598 return false;
600 // We don't support a root directory.
601 if (file_info->RootDirectory)
602 return false;
604 static const wchar_t kPathPrefix[] = { L'\\', L'?', L'?', L'\\'};
606 // Check if it starts with \\??\\. We don't support relative paths.
607 if (file_info->FileNameLength < sizeof(kPathPrefix) ||
608 file_info->FileNameLength > kuint16max)
609 return false;
611 if (file_info->FileName[0] != kPathPrefix[0] ||
612 file_info->FileName[1] != kPathPrefix[1] ||
613 file_info->FileName[2] != kPathPrefix[2] ||
614 file_info->FileName[3] != kPathPrefix[3])
615 return false;
617 return true;
620 } // namespace sandbox
622 void* operator new(size_t size, sandbox::AllocationType type,
623 void* near_to) {
624 using namespace sandbox;
626 void* result = NULL;
627 if (NT_ALLOC == type) {
628 if (InitHeap()) {
629 // Use default flags for the allocation.
630 result = g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size);
632 } else if (NT_PAGE == type) {
633 result = AllocateNearTo(near_to, size);
634 } else {
635 NOTREACHED_NT();
638 // TODO: Returning NULL from operator new has undefined behavior, but
639 // the Allocate() functions called above can return NULL. Consider checking
640 // for NULL here and crashing or throwing.
642 return result;
645 void operator delete(void* memory, sandbox::AllocationType type) {
646 using namespace sandbox;
648 if (NT_ALLOC == type) {
649 // Use default flags.
650 VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory));
651 } else if (NT_PAGE == type) {
652 void* base = memory;
653 SIZE_T size = 0;
654 VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
655 MEM_RELEASE));
656 } else {
657 NOTREACHED_NT();
661 void operator delete(void* memory, sandbox::AllocationType type,
662 void* near_to) {
663 UNREFERENCED_PARAMETER(near_to);
664 operator delete(memory, type);
667 void* __cdecl operator new(size_t size, void* buffer,
668 sandbox::AllocationType type) {
669 UNREFERENCED_PARAMETER(size);
670 UNREFERENCED_PARAMETER(type);
671 return buffer;
674 void __cdecl operator delete(void* memory, void* buffer,
675 sandbox::AllocationType type) {
676 UNREFERENCED_PARAMETER(memory);
677 UNREFERENCED_PARAMETER(buffer);
678 UNREFERENCED_PARAMETER(type);