summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc')
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc755
1 files changed, 755 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
new file mode 100644
index 0000000000..2641a13d04
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
@@ -0,0 +1,755 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+} // namespace sandbox
+
+namespace {
+
+#if defined(_WIN64)
+// Align a pointer to the next allocation granularity boundary.
+inline char* AlignToBoundary(void* ptr, size_t increment) {
+ const size_t kAllocationGranularity = (64 * 1024) - 1;
+ uintptr_t ptr_int = reinterpret_cast<uintptr_t>(ptr);
+ uintptr_t ret_ptr =
+ (ptr_int + increment + kAllocationGranularity) & ~kAllocationGranularity;
+ // Check for overflow.
+ if (ret_ptr < ptr_int)
+ return nullptr;
+ return reinterpret_cast<char*>(ret_ptr);
+}
+
+// Allocate a memory block somewhere within 2GiB of a specified base address.
+// This is used for the DLL hooking code to get a valid trampoline location
+// which must be within +/- 2GiB of the base. We only consider +2GiB for now.
+void* AllocateNearTo(void* source, size_t size) {
+ using sandbox::g_nt;
+ // 2GiB, maximum upper bound the allocation address must be within.
+ const size_t kMaxSize = 0x80000000ULL;
+ // We don't support null as a base as this would just pick an arbitrary
+ // address when passed to NtAllocateVirtualMemory.
+ if (!source)
+ return nullptr;
+ // Ignore an allocation which is larger than the maximum.
+ if (size > kMaxSize)
+ return nullptr;
+
+ // Ensure base address is aligned to the allocation granularity boundary.
+ char* base = AlignToBoundary(source, 0);
+ if (!base)
+ return nullptr;
+ // Set top address to be base + 2GiB.
+ const char* top_address = base + kMaxSize;
+
+ while (base < top_address) {
+ // Avoid memset inserted by -ftrivial-auto-var-init=pattern.
+ STACK_UNINITIALIZED MEMORY_BASIC_INFORMATION mem_info;
+ NTSTATUS status =
+ g_nt.QueryVirtualMemory(NtCurrentProcess, base, MemoryBasicInformation,
+ &mem_info, sizeof(mem_info), nullptr);
+ if (!NT_SUCCESS(status))
+ break;
+
+ if ((mem_info.State == MEM_FREE) && (mem_info.RegionSize >= size)) {
+ // We've found a valid free block, try and allocate it for use.
+ // Note that we need to both commit and reserve the block for the
+ // allocation to succeed as per Windows virtual memory requirements.
+ void* ret_base = mem_info.BaseAddress;
+ status =
+ g_nt.AllocateVirtualMemory(NtCurrentProcess, &ret_base, 0, &size,
+ MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ // Shouldn't fail, but if it does we'll just continue and try next block.
+ if (NT_SUCCESS(status))
+ return ret_base;
+ }
+
+ // Update base past current allocation region.
+ base = AlignToBoundary(mem_info.BaseAddress, mem_info.RegionSize);
+ if (!base)
+ break;
+ }
+ return nullptr;
+}
+#else // defined(_WIN64).
+void* AllocateNearTo(void* source, size_t size) {
+ using sandbox::g_nt;
+
+ // In 32-bit processes allocations below 512k are predictable, so mark
+ // anything in that range as reserved and retry until we get a good address.
+ const void* const kMinAddress = reinterpret_cast<void*>(512 * 1024);
+ NTSTATUS ret;
+ SIZE_T actual_size;
+ void* base;
+ do {
+ base = nullptr;
+ actual_size = 64 * 1024;
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
+ MEM_RESERVE, PAGE_NOACCESS);
+ if (!NT_SUCCESS(ret))
+ return nullptr;
+ } while (base < kMinAddress);
+
+ actual_size = size;
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (!NT_SUCCESS(ret))
+ return nullptr;
+ return base;
+}
+#endif // defined(_WIN64).
+
+} // namespace.
+
+namespace sandbox {
+
+// Handle for our private heap.
+void* g_heap = nullptr;
+
+SANDBOX_INTERCEPT HANDLE g_shared_section;
+SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0;
+SANDBOX_INTERCEPT size_t g_shared_policy_size = 0;
+
+void* volatile g_shared_policy_memory = nullptr;
+void* volatile g_shared_IPC_memory = nullptr;
+
+// Both the IPC and the policy share a single region of memory in which the IPC
+// memory is first and the policy memory is last.
+bool MapGlobalMemory() {
+ if (!g_shared_IPC_memory) {
+ void* memory = nullptr;
+ SIZE_T size = 0;
+ // Map the entire shared section from the start.
+ NTSTATUS ret =
+ g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess, &memory, 0, 0,
+ nullptr, &size, ViewUnmap, 0, PAGE_READWRITE);
+
+ if (!NT_SUCCESS(ret) || !memory) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ if (_InterlockedCompareExchangePointer(&g_shared_IPC_memory, memory,
+ nullptr)) {
+ // Somebody beat us to the memory setup.
+ VERIFY_SUCCESS(g_nt.UnmapViewOfSection(NtCurrentProcess, memory));
+ }
+ DCHECK_NT(g_shared_IPC_size > 0);
+ g_shared_policy_memory =
+ reinterpret_cast<char*>(g_shared_IPC_memory) + g_shared_IPC_size;
+ }
+ DCHECK_NT(g_shared_policy_memory);
+ DCHECK_NT(g_shared_policy_size > 0);
+ return true;
+}
+
+void* GetGlobalIPCMemory() {
+ if (!MapGlobalMemory())
+ return nullptr;
+ return g_shared_IPC_memory;
+}
+
+void* GetGlobalPolicyMemory() {
+ if (!MapGlobalMemory())
+ return nullptr;
+ return g_shared_policy_memory;
+}
+
+bool InitHeap() {
+ if (!g_heap) {
+ // Create a new heap using default values for everything.
+ void* heap =
+ g_nt.RtlCreateHeap(HEAP_GROWABLE, nullptr, 0, 0, nullptr, nullptr);
+ if (!heap)
+ return false;
+
+ if (_InterlockedCompareExchangePointer(&g_heap, heap, nullptr)) {
+ // Somebody beat us to the memory setup.
+ g_nt.RtlDestroyHeap(heap);
+ }
+ }
+ return !!g_heap;
+}
+
+// Physically reads or writes from memory to verify that (at this time), it is
+// valid. Returns a dummy value.
+int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) {
+ const int kPageSize = 4096;
+ int dummy = 0;
+ volatile char* start = reinterpret_cast<char*>(buffer);
+ volatile char* end = start + size_bytes - 1;
+
+ if (WRITE == intent) {
+ for (; start < end; start += kPageSize) {
+ *start = *start;
+ }
+ *end = *end;
+ } else {
+ for (; start < end; start += kPageSize) {
+ dummy += *start;
+ }
+ dummy += *end;
+ }
+
+ return dummy;
+}
+
+bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) {
+ DCHECK_NT(size);
+ __try {
+ TouchMemory(buffer, size, intent);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+ return true;
+}
+
+NTSTATUS CopyData(void* destination, const void* source, size_t bytes) {
+ NTSTATUS ret = STATUS_SUCCESS;
+ __try {
+ g_nt.memcpy(destination, source, bytes);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+ return ret;
+}
+
+NTSTATUS AllocAndGetFullPath(
+ HANDLE root,
+ const wchar_t* path,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* full_path) {
+ if (!InitHeap())
+ return STATUS_NO_MEMORY;
+
+ DCHECK_NT(full_path);
+ DCHECK_NT(path);
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+ __try {
+ do {
+ static NtQueryObjectFunction NtQueryObject = nullptr;
+ if (!NtQueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+ ULONG size = 0;
+ // Query the name information a first time to get the size of the name.
+ ret = NtQueryObject(root, ObjectNameInformation, nullptr, 0, &size);
+
+ std::unique_ptr<OBJECT_NAME_INFORMATION, NtAllocDeleter> handle_name;
+ if (size) {
+ handle_name.reset(reinterpret_cast<OBJECT_NAME_INFORMATION*>(
+ new (NT_ALLOC) BYTE[size]));
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ ret = NtQueryObject(root, ObjectNameInformation, handle_name.get(),
+ size, &size);
+ }
+
+ if (STATUS_SUCCESS != ret)
+ break;
+
+ // Space for path + '\' + name + '\0'.
+ size_t name_length =
+ handle_name->ObjectName.Length + (wcslen(path) + 2) * sizeof(wchar_t);
+ full_path->reset(new (NT_ALLOC) wchar_t[name_length / sizeof(wchar_t)]);
+ if (!*full_path)
+ break;
+ wchar_t* off = full_path->get();
+ ret = CopyData(off, handle_name->ObjectName.Buffer,
+ handle_name->ObjectName.Length);
+ if (!NT_SUCCESS(ret))
+ break;
+ off += handle_name->ObjectName.Length / sizeof(wchar_t);
+ *off = L'\\';
+ off += 1;
+ ret = CopyData(off, path, wcslen(path) * sizeof(wchar_t));
+ if (!NT_SUCCESS(ret))
+ break;
+ off += wcslen(path);
+ *off = L'\0';
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(ret) && *full_path)
+ full_path->reset(nullptr);
+
+ return ret;
+}
+
+// Hacky code... replace with AllocAndCopyObjectAttributes.
+NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* out_name,
+ uint32_t* attributes,
+ HANDLE* root) {
+ if (!InitHeap())
+ return STATUS_NO_MEMORY;
+
+ DCHECK_NT(out_name);
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+ __try {
+ do {
+ if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root)
+ break;
+ if (!in_object->ObjectName)
+ break;
+ if (!in_object->ObjectName->Buffer)
+ break;
+
+ size_t size = in_object->ObjectName->Length + sizeof(wchar_t);
+ out_name->reset(new (NT_ALLOC) wchar_t[size / sizeof(wchar_t)]);
+ if (!*out_name)
+ break;
+
+ ret = CopyData(out_name->get(), in_object->ObjectName->Buffer,
+ size - sizeof(wchar_t));
+ if (!NT_SUCCESS(ret))
+ break;
+
+ out_name->get()[size / sizeof(wchar_t) - 1] = L'\0';
+
+ if (attributes)
+ *attributes = in_object->Attributes;
+
+ if (root)
+ *root = in_object->RootDirectory;
+ ret = STATUS_SUCCESS;
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(ret) && *out_name)
+ out_name->reset(nullptr);
+
+ return ret;
+}
+
+NTSTATUS GetProcessId(HANDLE process, DWORD* process_id) {
+ PROCESS_BASIC_INFORMATION proc_info;
+ ULONG bytes_returned;
+
+ NTSTATUS ret =
+ g_nt.QueryInformationProcess(process, ProcessBasicInformation, &proc_info,
+ sizeof(proc_info), &bytes_returned);
+ if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned)
+ return ret;
+
+ *process_id = proc_info.UniqueProcessId;
+ return STATUS_SUCCESS;
+}
+
+bool IsSameProcess(HANDLE process) {
+ if (NtCurrentProcess == process)
+ return true;
+
+ static DWORD s_process_id = 0;
+
+ if (!s_process_id) {
+ NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id);
+ if (!NT_SUCCESS(ret))
+ return false;
+ }
+
+ DWORD process_id;
+ NTSTATUS ret = GetProcessId(process, &process_id);
+ if (!NT_SUCCESS(ret))
+ return false;
+
+ return (process_id == s_process_id);
+}
+
+bool IsValidImageSection(HANDLE section,
+ PVOID* base,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size) {
+ if (!section || !base || !view_size || offset)
+ return false;
+
+ HANDLE query_section;
+
+ NTSTATUS ret =
+ g_nt.DuplicateObject(NtCurrentProcess, section, NtCurrentProcess,
+ &query_section, SECTION_QUERY, 0, 0);
+ if (!NT_SUCCESS(ret))
+ return false;
+
+ SECTION_BASIC_INFORMATION basic_info;
+ SIZE_T bytes_returned;
+ ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info,
+ sizeof(basic_info), &bytes_returned);
+
+ VERIFY_SUCCESS(g_nt.Close(query_section));
+
+ if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned)
+ return false;
+
+ if (!(basic_info.Attributes & SEC_IMAGE))
+ return false;
+
+ // Windows 10 2009+ may open PEs as SEC_IMAGE_NO_EXECUTE in non-dll-loading
+ // paths which looks identical to dll-loading unless we check if the section
+ // handle has execute rights.
+ // Avoid memset inserted by -ftrivial-auto-var-init=pattern.
+ STACK_UNINITIALIZED OBJECT_BASIC_INFORMATION obj_info;
+ ULONG obj_size_returned;
+ ret = g_nt.QueryObject(section, ObjectBasicInformation, &obj_info,
+ sizeof(obj_info), &obj_size_returned);
+
+ if (!NT_SUCCESS(ret) || sizeof(obj_info) != obj_size_returned)
+ return false;
+
+ if (!(obj_info.GrantedAccess & SECTION_MAP_EXECUTE))
+ return false;
+
+ return true;
+}
+
+UNICODE_STRING* AnsiToUnicode(const char* string) {
+ ANSI_STRING ansi_string;
+ ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string));
+ ansi_string.MaximumLength = ansi_string.Length + 1;
+ ansi_string.Buffer = const_cast<char*>(string);
+
+ if (ansi_string.Length > ansi_string.MaximumLength)
+ return nullptr;
+
+ size_t name_bytes =
+ ansi_string.MaximumLength * sizeof(wchar_t) + sizeof(UNICODE_STRING);
+
+ UNICODE_STRING* out_string =
+ reinterpret_cast<UNICODE_STRING*>(new (NT_ALLOC) char[name_bytes]);
+ if (!out_string)
+ return nullptr;
+
+ out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t);
+ out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+
+ BOOLEAN alloc_destination = false;
+ NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string,
+ alloc_destination);
+ DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret);
+ if (!NT_SUCCESS(ret)) {
+ operator delete(out_string, NT_ALLOC);
+ return nullptr;
+ }
+
+ return out_string;
+}
+
+UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* flags) {
+// PEImage's dtor won't be run during SEH unwinding, but that's OK.
+#pragma warning(push)
+#pragma warning(disable : 4509)
+ UNICODE_STRING* out_name = nullptr;
+ __try {
+ do {
+ *flags = 0;
+ base::win::PEImage pe(module);
+
+ if (!pe.VerifyMagic())
+ break;
+ *flags |= MODULE_IS_PE_IMAGE;
+
+ PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
+ if (exports) {
+ char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name));
+ out_name = AnsiToUnicode(name);
+ }
+
+ PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
+ if (headers) {
+ if (headers->OptionalHeader.AddressOfEntryPoint)
+ *flags |= MODULE_HAS_ENTRY_POINT;
+ if (headers->OptionalHeader.SizeOfCode)
+ *flags |= MODULE_HAS_CODE;
+ }
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+
+ return out_name;
+#pragma warning(pop)
+}
+
+const char* GetAnsiImageInfoFromModule(HMODULE module) {
+// PEImage's dtor won't be run during SEH unwinding, but that's OK.
+#pragma warning(push)
+#pragma warning(disable : 4509)
+ const char* out_name = nullptr;
+ __try {
+ do {
+ base::win::PEImage pe(module);
+
+ if (!pe.VerifyMagic())
+ break;
+
+ PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
+ if (exports)
+ out_name = static_cast<const char*>(pe.RVAToAddr(exports->Name));
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+
+ return out_name;
+#pragma warning(pop)
+}
+
+UNICODE_STRING* GetBackingFilePath(PVOID address) {
+ // We'll start with something close to max_path charactes for the name.
+ SIZE_T buffer_bytes = MAX_PATH * 2;
+
+ for (;;) {
+ MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>(
+ new (NT_ALLOC) char[buffer_bytes]);
+
+ if (!section_name)
+ return nullptr;
+
+ SIZE_T returned_bytes;
+ NTSTATUS ret =
+ g_nt.QueryVirtualMemory(NtCurrentProcess, address, MemorySectionName,
+ section_name, buffer_bytes, &returned_bytes);
+
+ if (STATUS_BUFFER_OVERFLOW == ret) {
+ // Retry the call with the given buffer size.
+ operator delete(section_name, NT_ALLOC);
+ section_name = nullptr;
+ buffer_bytes = returned_bytes;
+ continue;
+ }
+ if (!NT_SUCCESS(ret)) {
+ operator delete(section_name, NT_ALLOC);
+ return nullptr;
+ }
+
+ return reinterpret_cast<UNICODE_STRING*>(section_name);
+ }
+}
+
+UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) {
+ if ((!module_path) || (!module_path->Buffer))
+ return nullptr;
+
+ wchar_t* sep = nullptr;
+ int start_pos = module_path->Length / sizeof(wchar_t) - 1;
+ int ix = start_pos;
+
+ for (; ix >= 0; --ix) {
+ if (module_path->Buffer[ix] == L'\\') {
+ sep = &module_path->Buffer[ix];
+ break;
+ }
+ }
+
+ // Ends with path separator. Not a valid module name.
+ if ((ix == start_pos) && sep)
+ return nullptr;
+
+ // No path separator found. Use the entire name.
+ if (!sep) {
+ sep = &module_path->Buffer[-1];
+ }
+
+ // Add one to the size so we can null terminate the string.
+ size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t);
+
+ // Based on the code above, size_bytes should always be small enough
+ // to make the static_cast below safe.
+ DCHECK_NT(UINT16_MAX > size_bytes);
+ char* str_buffer = new (NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)];
+ if (!str_buffer)
+ return nullptr;
+
+ UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer);
+ out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+ out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t));
+ out_string->MaximumLength = static_cast<USHORT>(size_bytes);
+
+ NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length);
+ if (!NT_SUCCESS(ret)) {
+ operator delete(out_string, NT_ALLOC);
+ return nullptr;
+ }
+
+ out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0';
+ return out_string;
+}
+
+NTSTATUS AutoProtectMemory::ChangeProtection(void* address,
+ size_t bytes,
+ ULONG protect) {
+ DCHECK_NT(!changed_);
+ SIZE_T new_bytes = bytes;
+ NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address,
+ &new_bytes, protect, &old_protect_);
+ if (NT_SUCCESS(ret)) {
+ changed_ = true;
+ address_ = address;
+ bytes_ = new_bytes;
+ }
+
+ return ret;
+}
+
+NTSTATUS AutoProtectMemory::RevertProtection() {
+ if (!changed_)
+ return STATUS_SUCCESS;
+
+ DCHECK_NT(address_);
+ DCHECK_NT(bytes_);
+
+ SIZE_T new_bytes = bytes_;
+ NTSTATUS ret = g_nt.ProtectVirtualMemory(
+ NtCurrentProcess, &address_, &new_bytes, old_protect_, &old_protect_);
+ DCHECK_NT(NT_SUCCESS(ret));
+
+ changed_ = false;
+ address_ = nullptr;
+ bytes_ = 0;
+ old_protect_ = 0;
+
+ return ret;
+}
+
+bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info,
+ DWORD length,
+ uint32_t file_info_class) {
+ if (FileRenameInformation != file_info_class)
+ return false;
+
+ if (length < sizeof(FILE_RENAME_INFORMATION))
+ return false;
+
+ // Make sure file name length doesn't exceed the message length
+ if (length - offsetof(FILE_RENAME_INFORMATION, FileName) <
+ file_info->FileNameLength)
+ return false;
+
+ // We don't support a root directory.
+ if (file_info->RootDirectory)
+ return false;
+
+ static const wchar_t kPathPrefix[] = {L'\\', L'?', L'?', L'\\'};
+
+ // Check if it starts with \\??\\. We don't support relative paths.
+ if (file_info->FileNameLength < sizeof(kPathPrefix) ||
+ file_info->FileNameLength > UINT16_MAX)
+ return false;
+
+ if (file_info->FileName[0] != kPathPrefix[0] ||
+ file_info->FileName[1] != kPathPrefix[1] ||
+ file_info->FileName[2] != kPathPrefix[2] ||
+ file_info->FileName[3] != kPathPrefix[3])
+ return false;
+
+ return true;
+}
+
+bool NtGetPathFromHandle(HANDLE handle,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* path) {
+ OBJECT_NAME_INFORMATION initial_buffer;
+ OBJECT_NAME_INFORMATION* name;
+ ULONG size = 0;
+ // Query the name information a first time to get the size of the name.
+ NTSTATUS status = g_nt.QueryObject(handle, ObjectNameInformation,
+ &initial_buffer, size, &size);
+
+ if (!NT_SUCCESS(status) && status != STATUS_INFO_LENGTH_MISMATCH)
+ return false;
+
+ std::unique_ptr<BYTE[], NtAllocDeleter> name_ptr;
+ if (!size)
+ return false;
+ name_ptr.reset(new (NT_ALLOC) BYTE[size]);
+ name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(name_ptr.get());
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ status = g_nt.QueryObject(handle, ObjectNameInformation, name, size, &size);
+
+ if (STATUS_SUCCESS != status)
+ return false;
+ size_t num_path_wchars = (name->ObjectName.Length / sizeof(wchar_t)) + 1;
+ path->reset(new (NT_ALLOC) wchar_t[num_path_wchars]);
+ status =
+ CopyData(path->get(), name->ObjectName.Buffer, name->ObjectName.Length);
+ path->get()[num_path_wchars - 1] = L'\0';
+ if (STATUS_SUCCESS != status)
+ return false;
+ return true;
+}
+
+} // namespace sandbox
+
+void* operator new(size_t size, sandbox::AllocationType type, void* near_to) {
+ void* result = nullptr;
+ if (type == sandbox::NT_ALLOC) {
+ if (sandbox::InitHeap()) {
+ // Use default flags for the allocation.
+ result = sandbox::g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size);
+ }
+ } else if (type == sandbox::NT_PAGE) {
+ result = AllocateNearTo(near_to, size);
+ } else {
+ NOTREACHED_NT();
+ }
+
+ // TODO: Returning nullptr from operator new has undefined behavior, but
+ // the Allocate() functions called above can return nullptr. Consider checking
+ // for nullptr here and crashing or throwing.
+
+ return result;
+}
+
+void* operator new [](size_t size, sandbox::AllocationType type,
+ void* near_to) {
+ return operator new(size, type, near_to);
+}
+
+void operator delete(void* memory, sandbox::AllocationType type) {
+ if (type == sandbox::NT_ALLOC) {
+ // Use default flags.
+ VERIFY(sandbox::g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory));
+ } else if (type == sandbox::NT_PAGE) {
+ void* base = memory;
+ SIZE_T size = 0;
+ VERIFY_SUCCESS(sandbox::g_nt.FreeVirtualMemory(NtCurrentProcess, &base,
+ &size, MEM_RELEASE));
+ } else {
+ NOTREACHED_NT();
+ }
+}
+
+void operator delete(void* memory,
+ sandbox::AllocationType type,
+ void* near_to) {
+ operator delete(memory, type);
+}
+
+void* __cdecl operator new(size_t size,
+ void* buffer,
+ sandbox::AllocationType type) {
+ return buffer;
+}
+
+void __cdecl operator delete(void* memory,
+ void* buffer,
+ sandbox::AllocationType type) {}