| Index: sandbox/win/src/sandbox_nt_util.cc
|
| diff --git a/sandbox/win/src/sandbox_nt_util.cc b/sandbox/win/src/sandbox_nt_util.cc
|
| index 62f2422ca4324f3e9f6c6fa2140001353c9ca535..ac73fc11c3f7c1077097e67b8a19685d46cb3b88 100644
|
| --- a/sandbox/win/src/sandbox_nt_util.cc
|
| +++ b/sandbox/win/src/sandbox_nt_util.cc
|
| @@ -23,58 +23,67 @@ SANDBOX_INTERCEPT NtExports g_nt;
|
| 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;
|
| -
|
| - // Start with 1 GB above the source.
|
| - const size_t kOneGB = 0x40000000;
|
| - void* base = reinterpret_cast<char*>(source) + kOneGB;
|
| - SIZE_T actual_size = size;
|
| - ULONG_PTR zero_bits = 0; // Not the correct type if used.
|
| - ULONG type = MEM_RESERVE;
|
| -
|
| - NTSTATUS ret;
|
| - int attempts = 0;
|
| - for (; attempts < 41; attempts++) {
|
| - ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
|
| - &actual_size, type, PAGE_READWRITE);
|
| - if (NT_SUCCESS(ret)) {
|
| - if (base < source ||
|
| - base >= reinterpret_cast<char*>(source) + 4 * kOneGB) {
|
| - // We won't be able to patch this dll.
|
| - VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
|
| - MEM_RELEASE));
|
| - return NULL;
|
| - }
|
| + // 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 == nullptr)
|
| + 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 == nullptr)
|
| + return nullptr;
|
| + // Set top address to be base + 2GiB.
|
| + const char* top_address = base + kMaxSize;
|
| +
|
| + while (base < top_address) {
|
| + 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 (attempts == 30) {
|
| - // Try the first GB.
|
| - base = reinterpret_cast<char*>(source);
|
| - } else if (attempts == 40) {
|
| - // Try the highest available address.
|
| - base = NULL;
|
| - type |= MEM_TOP_DOWN;
|
| + 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;
|
| }
|
|
|
| - // Try 100 MB higher.
|
| - base = reinterpret_cast<char*>(base) + 100 * 0x100000;
|
| - }
|
| -
|
| - if (attempts == 41)
|
| - return NULL;
|
| -
|
| - ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
|
| - &actual_size, MEM_COMMIT, PAGE_READWRITE);
|
| -
|
| - if (!NT_SUCCESS(ret)) {
|
| - VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
|
| - MEM_RELEASE));
|
| - base = NULL;
|
| + // Update base past current allocation region.
|
| + base = AlignToBoundary(mem_info.BaseAddress, mem_info.RegionSize);
|
| + if (base == nullptr)
|
| + break;
|
| }
|
| -
|
| - return base;
|
| + return nullptr;
|
| }
|
| #else // defined(_WIN64).
|
| void* AllocateNearTo(void* source, size_t size) {
|
|
|