VirtualAlloc in specific memory range (Windows, x64)

224 Views Asked by At

I am trying to allocate a memory region with a specified maximum absolute distance from a given base address.

For example, let's say the base address is 0x007FFFFFFF400000 and I want to allocate a block of memory with size size that is reachable through a relative jump with 32-bit displacement. That means the maximum allocation address would be 0x007FFFFFFF400000 + (1LL << 31) - size (maybe a bit larger since the relative jump would count the displacement from after the jump instruction, but I safely underestimate).

I have written the following code to try accomplish this:

void* allocate_around_2gb(const void* base_address, const size_t size) {
    const auto base = reinterpret_cast<uintptr_t>(base_address);
    const auto num_pages_required = static_cast<size_t>(std::ceil((double)size / SystemInfo::page_size));

    // Align to allocation boundary for VirtualQuery
    const auto base_aligned = base - (base % SystemInfo::allocation_granularity);

    // Maximum allocation address
    const auto high_bound = base + (1ULL << 31) - size;

    // Start searching from the next 64 KB region, since VirtualAlloc
    // reserves 64 KB-aligned regions with MEM_RESERVE and non-NULL `lpAddress`
    auto curr = base_aligned + SystemInfo::allocation_granularity;

    bool found = false;

    // Search for higher addresses, left out the backwards search for clarity
    while (curr < high_bound) {
        MEMORY_BASIC_INFORMATION mbi;
        std::memset(&mbi, 0, sizeof(mbi));

        if (VirtualQuery(reinterpret_cast<void*>(curr), &mbi, sizeof(mbi))) {
            // If we find a MEM_FREE region with enough space, use it
            if ((mbi.State & MEM_FREE) && mbi.RegionSize >= (num_pages_required * SystemInfo::page_size)) {
                found = true;
                break;
            }
        }

        // `curr` is always aligned to the allocation granularity (64 KB)
        curr += SystemInfo::allocation_granularity;
    }

    if (!found) {
        return nullptr;
    }

    return VirtualAlloc(reinterpret_cast<void*>(curr), size,
        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
}

However, I am always getting ERROR_INVALID_ADDRESS as the VirtualAlloc error code. Any ideas why this could be, even though the region is MEM_FREE and has the right size?

0

There are 0 best solutions below